summaryrefslogtreecommitdiff
path: root/indra/llui
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llui')
-rw-r--r--indra/llui/CMakeLists.txt12
-rw-r--r--indra/llui/llaccordionctrltab.cpp2
-rw-r--r--indra/llui/llbutton.cpp84
-rw-r--r--indra/llui/llbutton.h8
-rw-r--r--indra/llui/llchatentry.cpp228
-rw-r--r--indra/llui/llchatentry.h103
-rw-r--r--indra/llui/llcheckboxctrl.cpp14
-rw-r--r--indra/llui/llcheckboxctrl.h2
-rw-r--r--indra/llui/llcommandmanager.cpp2
-rw-r--r--indra/llui/llcommandmanager.h6
-rw-r--r--indra/llui/lldockcontrol.cpp31
-rw-r--r--indra/llui/lldockcontrol.h4
-rw-r--r--indra/llui/llflashtimer.cpp100
-rw-r--r--indra/llui/llflashtimer.h73
-rw-r--r--indra/llui/llfloater.cpp188
-rw-r--r--indra/llui/llfloater.h19
-rw-r--r--indra/llui/llfloaterreg.cpp73
-rw-r--r--indra/llui/llfolderview.cpp1954
-rw-r--r--indra/llui/llfolderview.h399
-rwxr-xr-xindra/llui/llfolderviewitem.cpp2095
-rwxr-xr-xindra/llui/llfolderviewitem.h458
-rw-r--r--indra/llui/llfolderviewmodel.cpp68
-rw-r--r--indra/llui/llfolderviewmodel.h444
-rw-r--r--indra/llui/lllayoutstack.cpp50
-rw-r--r--indra/llui/lllayoutstack.h4
-rw-r--r--indra/llui/lllineeditor.cpp9
-rw-r--r--indra/llui/lllineeditor.h3
-rw-r--r--indra/llui/llloadingindicator.cpp2
-rw-r--r--indra/llui/llloadingindicator.h6
-rw-r--r--indra/llui/llmenubutton.cpp78
-rw-r--r--indra/llui/llmenubutton.h11
-rw-r--r--indra/llui/llmenugl.cpp109
-rw-r--r--indra/llui/llmenugl.h9
-rw-r--r--indra/llui/llmultifloater.cpp45
-rw-r--r--indra/llui/llmultifloater.h16
-rw-r--r--indra/llui/llnotifications.cpp498
-rw-r--r--indra/llui/llnotifications.h292
-rw-r--r--indra/llui/llnotificationslistener.cpp354
-rw-r--r--indra/llui/llnotificationslistener.h69
-rw-r--r--indra/llui/llnotificationtemplate.h35
-rw-r--r--indra/llui/llresizebar.cpp8
-rw-r--r--indra/llui/llresizebar.h2
-rw-r--r--indra/llui/llscrolllistctrl.cpp2
-rw-r--r--indra/llui/llspinctrl.cpp5
-rw-r--r--indra/llui/llspinctrl.h1
-rw-r--r--indra/llui/lltabcontainer.cpp12
-rw-r--r--indra/llui/lltabcontainer.h5
-rw-r--r--indra/llui/lltextbase.cpp166
-rw-r--r--indra/llui/lltextbase.h57
-rw-r--r--indra/llui/lltexteditor.cpp51
-rw-r--r--indra/llui/lltexteditor.h14
-rw-r--r--indra/llui/lltoggleablemenu.h2
-rw-r--r--indra/llui/lltoolbar.cpp13
-rw-r--r--indra/llui/lltooltip.cpp2
-rw-r--r--indra/llui/llui.cpp243
-rw-r--r--indra/llui/llui.h14
-rw-r--r--indra/llui/lluictrlfactory.cpp18
-rw-r--r--indra/llui/lluictrlfactory.h5
-rw-r--r--indra/llui/lluiimage.cpp48
-rw-r--r--indra/llui/lluiimage.h8
-rw-r--r--indra/llui/llurlaction.cpp14
-rw-r--r--indra/llui/llurlaction.h1
-rw-r--r--indra/llui/llurlentry.cpp32
-rw-r--r--indra/llui/llurlentry.h16
-rw-r--r--indra/llui/llview.cpp34
-rw-r--r--indra/llui/llview.h1
-rw-r--r--indra/llui/llxuiparser.cpp92
-rw-r--r--indra/llui/llxuiparser.h2
-rw-r--r--indra/llui/tests/llurlentry_stub.cpp21
-rw-r--r--indra/llui/tests/llurlmatch_test.cpp16
70 files changed, 7565 insertions, 1297 deletions
diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt
index d92b6aa1c0..9c961d67d6 100644
--- a/indra/llui/CMakeLists.txt
+++ b/indra/llui/CMakeLists.txt
@@ -33,6 +33,7 @@ set(llui_SOURCE_FILES
llbadgeholder.cpp
llbadgeowner.cpp
llbutton.cpp
+ llchatentry.cpp
llcheckboxctrl.cpp
llclipboard.cpp
llcombobox.cpp
@@ -46,12 +47,16 @@ set(llui_SOURCE_FILES
lleditmenuhandler.cpp
llf32uictrl.cpp
llfiltereditor.cpp
+ llflashtimer.cpp
llflatlistview.cpp
llfloater.cpp
llfloaterreg.cpp
llfloaterreglistener.cpp
llflyoutbutton.cpp
llfocusmgr.cpp
+ llfolderview.cpp
+ llfolderviewitem.cpp
+ llfolderviewmodel.cpp
llfunctorregistry.cpp
lliconctrl.cpp
llkeywords.cpp
@@ -66,7 +71,6 @@ set(llui_SOURCE_FILES
llmultislider.cpp
llmultisliderctrl.cpp
llnotifications.cpp
- llnotificationslistener.cpp
llnotificationsutil.cpp
llpanel.cpp
llprogressbar.cpp
@@ -135,6 +139,7 @@ set(llui_HEADER_FILES
llbadgeowner.h
llbutton.h
llcallbackmap.h
+ llchatentry.h
llcheckboxctrl.h
llclipboard.h
llcombobox.h
@@ -148,12 +153,16 @@ set(llui_HEADER_FILES
lleditmenuhandler.h
llf32uictrl.h
llfiltereditor.h
+ llflashtimer.h
llflatlistview.h
llfloater.h
llfloaterreg.h
llfloaterreglistener.h
llflyoutbutton.h
llfocusmgr.h
+ llfolderview.h
+ llfolderviewitem.h
+ llfolderviewmodel.h
llfunctorregistry.h
llhelp.h
lliconctrl.h
@@ -171,7 +180,6 @@ set(llui_HEADER_FILES
llmultislider.h
llnotificationptr.h
llnotifications.h
- llnotificationslistener.h
llnotificationsutil.h
llnotificationtemplate.h
llnotificationvisibilityrule.h
diff --git a/indra/llui/llaccordionctrltab.cpp b/indra/llui/llaccordionctrltab.cpp
index c025cd7939..43462bd244 100644
--- a/indra/llui/llaccordionctrltab.cpp
+++ b/indra/llui/llaccordionctrltab.cpp
@@ -184,7 +184,7 @@ void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::setTitleFontStyle(std::string
if (mHeaderTextbox)
{
std::string text = mHeaderTextbox->getText();
- mStyleParams.font(mHeaderTextbox->getDefaultFont());
+ mStyleParams.font(mHeaderTextbox->getFont());
mStyleParams.font.style(style);
mHeaderTextbox->setText(text, mStyleParams);
}
diff --git a/indra/llui/llbutton.cpp b/indra/llui/llbutton.cpp
index 705fe16559..a8149a9a1d 100644
--- a/indra/llui/llbutton.cpp
+++ b/indra/llui/llbutton.cpp
@@ -105,6 +105,7 @@ LLButton::Params::Params()
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")
{
@@ -171,9 +172,24 @@ LLButton::LLButton(const LLButton::Params& p)
mHeldDownSignal(NULL),
mUseDrawContextAlpha(p.use_draw_context_alpha),
mHandleRightMouse(p.handle_right_mouse),
- mButtonFlashCount(p.button_flash_count),
- mButtonFlashRate(p.button_flash_rate)
+ 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<S32> llbutton_orig_h_pad ("UIButtonOrigHPad", 0);
static Params default_params(LLUICtrlFactory::getDefaultParams<LLButton>());
@@ -267,6 +283,11 @@ LLButton::~LLButton()
delete mMouseDownSignal;
delete mMouseUpSignal;
delete mHeldDownSignal;
+
+ if (mFlashingTimer)
+ {
+ mFlashingTimer->unset();
+ }
}
// HACK: Committing a button is the same as instantly clicking it.
@@ -591,22 +612,6 @@ void LLButton::draw()
{
static LLCachedControl<bool> sEnableButtonFlashing(*LLUI::sSettingGroups["config"], "EnableButtonFlashing", true);
F32 alpha = mUseDrawContextAlpha ? getDrawContext().mAlpha : getCurrentTransparency();
- bool flash = FALSE;
-
- if( mFlashing)
- {
- if ( sEnableButtonFlashing)
- {
- F32 elapsed = mFlashingTimer.getElapsedTimeF32();
- S32 flash_count = S32(elapsed * mButtonFlashRate * 2.f);
- // flash on or off?
- flash = (flash_count % 2 == 0) || flash_count > S32((F32)mButtonFlashCount * 2.f);
- }
- else
- { // otherwise just highlight button in flash color
- flash = true;
- }
- }
bool pressed_by_keyboard = FALSE;
if (hasFocus())
@@ -631,9 +636,21 @@ void LLButton::draw()
bool selected = getToggleState();
bool use_glow_effect = FALSE;
- LLColor4 glow_color = LLColor4::white;
+ LLColor4 highlighting_color = LLColor4::white;
+ LLColor4 glow_color;
LLRender::eBlendType glow_type = LLRender::BT_ADD_WITH_ALPHA;
LLUIImage* imagep = 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()) || pressed))
+ {
+ mFlashing = false;
+ }
+
+ bool flash = mFlashing && sEnableButtonFlashing;
+
if (pressed && mDisplayPressedState)
{
imagep = selected ? mImagePressedSelected : mImagePressed;
@@ -699,15 +716,20 @@ void LLButton::draw()
imagep = mImageFlash;
}
// else use usual flashing via flash_color
- else
+ else if (mFlashingTimer)
{
LLColor4 flash_color = mFlashBgColor.get();
use_glow_effect = TRUE;
glow_type = LLRender::BT_ALPHA; // blend the glow
- if (mNeedsHighlight) // highlighted AND flashing
- glow_color = (glow_color*0.5f + flash_color*0.5f) % 2.0f; // average between flash and highlight colour, with sum of the opacity
- else
+
+ if (mFlashingTimer->isCurrentlyHighlighted() || !mFlashingTimer->isFlashingInProgress())
+ {
glow_color = flash_color;
+ }
+ else if (mNeedsHighlight)
+ {
+ glow_color = highlighting_color;
+ }
}
}
@@ -756,8 +778,7 @@ void LLButton::draw()
if (use_glow_effect)
{
mCurGlowStrength = lerp(mCurGlowStrength,
- mFlashing ? (flash? 1.0 : 0.0)
- : mHoverGlowStrength,
+ mFlashing ? (mFlashingTimer->isCurrentlyHighlighted() || !mFlashingTimer->isFlashingInProgress() || mNeedsHighlight? 1.0 : 0.0) : mHoverGlowStrength,
LLCriticalDamp::getInterpolant(0.05f));
}
else
@@ -944,21 +965,26 @@ void LLButton::setToggleState(BOOL b)
{
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 )
+void LLButton::setFlashing(bool b)
{
- if ((bool)b != mFlashing)
+ if (mFlashingTimer)
{
mFlashing = b;
- mFlashingTimer.reset();
+ (b ? mFlashingTimer->startFlashing() : mFlashingTimer->stopFlashing());
+ }
+ else if (b != mFlashing)
+ {
+ mFlashing = b;
+ mFrameTimer.reset();
}
}
-
BOOL LLButton::toggleState()
{
bool flipped = ! getToggleState();
diff --git a/indra/llui/llbutton.h b/indra/llui/llbutton.h
index deaa0823c6..060db59a8a 100644
--- a/indra/llui/llbutton.h
+++ b/indra/llui/llbutton.h
@@ -30,6 +30,7 @@
#include "lluuid.h"
#include "llbadgeowner.h"
#include "llcontrol.h"
+#include "llflashtimer.h"
#include "lluictrl.h"
#include "v4color.h"
#include "llframetimer.h"
@@ -133,6 +134,7 @@ public:
Optional<bool> handle_right_mouse;
+ Optional<bool> button_flash_enable;
Optional<S32> button_flash_count;
Optional<F32> button_flash_rate;
@@ -199,8 +201,9 @@ public:
void setToggleState(BOOL b);
void setHighlight(bool b);
- void setFlashing( BOOL b );
+ void setFlashing( bool b );
BOOL getFlashing() const { return mFlashing; }
+ LLFlashTimer* getFlashTimer() {return mFlashingTimer;}
void setHAlign( LLFontGL::HAlign align ) { mHAlign = align; }
LLFontGL::HAlign getHAlign() const { return mHAlign; }
@@ -373,7 +376,8 @@ protected:
bool mForcePressedState;
bool mDisplayPressedState;
- LLFrameTimer mFlashingTimer;
+ LLFrameTimer mFrameTimer;
+ LLFlashTimer * mFlashingTimer;
bool mHandleRightMouse;
};
diff --git a/indra/llui/llchatentry.cpp b/indra/llui/llchatentry.cpp
new file mode 100644
index 0000000000..8e9c6555c3
--- /dev/null
+++ b/indra/llui/llchatentry.cpp
@@ -0,0 +1,228 @@
+/**
+ * @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 "llchatentry.h"
+
+static LLDefaultChildRegistry::Register<LLChatEntry> 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)
+{
+ // Initialize current history line iterator
+ mCurrentHistoryLine = mLineHistory.begin();
+
+ mAutoIndent = false;
+}
+
+LLChatEntry::~LLChatEntry()
+{
+ delete mTextExpandedSignal;
+}
+
+void LLChatEntry::draw()
+{
+ if(mIsExpandable)
+ {
+ 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()
+{
+ int visible_lines_count = llabs(getVisibleLines(true).first - getVisibleLines(true).second);
+ bool can_expand = getLineCount() <= mExpandLinesCount;
+
+ // true if pasted text has more lines than expand height limit and expand limit is not reached yet
+ bool text_pasted = (getLineCount() > mExpandLinesCount) && (visible_lines_count < mExpandLinesCount);
+
+ if (mIsExpandable && (can_expand || text_pasted) && 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 'mExpandLinesCount' lines height
+ lines_height = (mLineInfoList.end() - mExpandLinesCount)->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() );
+ }
+ }
+}
+
+// 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()
+{
+ return !getLength() && !mLabel.empty();
+}
+
+void LLChatEntry::onFocusReceived()
+{
+
+}
+
+void LLChatEntry::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::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::reportBadKeystroke();
+ }
+ handled = TRUE;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return handled;
+}
diff --git a/indra/llui/llchatentry.h b/indra/llui/llchatentry.h
new file mode 100644
index 0000000000..49181c8d78
--- /dev/null
+++ b/indra/llui/llchatentry.h
@@ -0,0 +1,103 @@
+/**
+ * @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<Params, LLTextEditor::Params>
+ {
+ Optional<bool> has_history,
+ is_expandable;
+
+ Optional<int> 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();
+
+public:
+
+ virtual void draw();
+ virtual void onCommit();
+ /*virtual*/ void onFocusReceived();
+ /*virtual*/ void onFocusLost();
+
+ 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<std::string> 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;
+
+ int mExpandLinesCount;
+ int mPrevLinesCount;
+};
+
+#endif /* LLCHATENTRY_H_ */
diff --git a/indra/llui/llcheckboxctrl.cpp b/indra/llui/llcheckboxctrl.cpp
index 4fe444c1a4..5525520d78 100644
--- a/indra/llui/llcheckboxctrl.cpp
+++ b/indra/llui/llcheckboxctrl.cpp
@@ -107,7 +107,7 @@ LLCheckBoxCtrl::LLCheckBoxCtrl(const LLCheckBoxCtrl::Params& p)
LLButton::Params params = p.check_button;
params.rect(btn_rect);
//params.control_name(p.control_name);
- params.click_callback.function(boost::bind(&LLCheckBoxCtrl::onButtonPress, this, _2));
+ 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.
@@ -123,18 +123,6 @@ LLCheckBoxCtrl::~LLCheckBoxCtrl()
// Children all cleaned up by default view destructor.
}
-
-// static
-void LLCheckBoxCtrl::onButtonPress( const LLSD& data )
-{
- //if (mRadioStyle)
- //{
- // setValue(TRUE);
- //}
-
- onCommit();
-}
-
void LLCheckBoxCtrl::onCommit()
{
if( getEnabled() )
diff --git a/indra/llui/llcheckboxctrl.h b/indra/llui/llcheckboxctrl.h
index 67d8091a97..5ce45b2135 100644
--- a/indra/llui/llcheckboxctrl.h
+++ b/indra/llui/llcheckboxctrl.h
@@ -103,8 +103,6 @@ public:
virtual void setControlName(const std::string& control_name, LLView* context);
- void onButtonPress(const LLSD& data);
-
virtual BOOL isDirty() const; // Returns TRUE if the user has modified this control.
virtual void resetDirty(); // Clear dirty state
diff --git a/indra/llui/llcommandmanager.cpp b/indra/llui/llcommandmanager.cpp
index 0e2f3f1961..625fb8e870 100644
--- a/indra/llui/llcommandmanager.cpp
+++ b/indra/llui/llcommandmanager.cpp
@@ -63,6 +63,7 @@ LLCommand::Params::Params()
, is_running_parameters("is_running_parameters")
, is_starting_function("is_starting_function")
, is_starting_parameters("is_starting_parameters")
+ , is_flashing_allowed("is_flashing_allowed", false)
{
}
@@ -83,6 +84,7 @@ LLCommand::LLCommand(const LLCommand::Params& p)
, mIsRunningParameters(p.is_running_parameters)
, mIsStartingFunction(p.is_starting_function)
, mIsStartingParameters(p.is_starting_parameters)
+ , mIsFlashingAllowed(p.is_flashing_allowed)
{
}
diff --git a/indra/llui/llcommandmanager.h b/indra/llui/llcommandmanager.h
index a7276a48aa..ff5a8a3257 100644
--- a/indra/llui/llcommandmanager.h
+++ b/indra/llui/llcommandmanager.h
@@ -111,6 +111,8 @@ public:
Optional<std::string> is_starting_function;
Optional<LLSD> is_starting_parameters;
+ Optional<bool> is_flashing_allowed;
+
Params();
};
@@ -138,6 +140,8 @@ public:
const std::string& isStartingFunctionName() const { return mIsStartingFunction; }
const LLSD& isStartingParameters() const { return mIsStartingParameters; }
+ bool isFlashingAllowed() const { return mIsFlashingAllowed; }
+
private:
LLCommandId mIdentifier;
@@ -161,6 +165,8 @@ private:
std::string mIsStartingFunction;
LLSD mIsStartingParameters;
+
+ bool mIsFlashingAllowed;
};
diff --git a/indra/llui/lldockcontrol.cpp b/indra/llui/lldockcontrol.cpp
index af39e41fa6..bd42497cb6 100644
--- a/indra/llui/lldockcontrol.cpp
+++ b/indra/llui/lldockcontrol.cpp
@@ -31,7 +31,6 @@
LLDockControl::LLDockControl(LLView* dockWidget, LLFloater* dockableFloater,
const LLUIImagePtr& dockTongue, DocAt dockAt, get_allowed_rect_callback_t get_allowed_rect_callback) :
- mDockWidget(dockWidget),
mDockableFloater(dockableFloater),
mDockTongue(dockTongue),
mDockTongueX(0),
@@ -39,6 +38,11 @@ LLDockControl::LLDockControl(LLView* dockWidget, LLFloater* dockableFloater,
{
mDockAt = dockAt;
+ if (dockWidget != NULL)
+ {
+ mDockWidgetHandle = dockWidget->getHandle();
+ }
+
if (dockableFloater->isDocked())
{
on();
@@ -62,7 +66,7 @@ LLDockControl::LLDockControl(LLView* dockWidget, LLFloater* dockableFloater,
repositionDockable();
}
- if (mDockWidget != NULL)
+ if (getDock() != NULL)
{
mDockWidgetVisible = isDockVisible();
}
@@ -78,14 +82,15 @@ LLDockControl::~LLDockControl()
void LLDockControl::setDock(LLView* dockWidget)
{
- mDockWidget = dockWidget;
- if (mDockWidget != NULL)
+ if (dockWidget != NULL)
{
+ mDockWidgetHandle = dockWidget->getHandle();
repositionDockable();
mDockWidgetVisible = isDockVisible();
}
else
{
+ mDockWidgetHandle = LLHandle<LLView>();
mDockWidgetVisible = false;
}
}
@@ -97,8 +102,8 @@ void LLDockControl::getAllowedRect(LLRect& rect)
void LLDockControl::repositionDockable()
{
- if (!mDockWidget) return;
- LLRect dockRect = mDockWidget->calcScreenRect();
+ if (!getDock()) return;
+ LLRect dockRect = getDock()->calcScreenRect();
LLRect rootRect;
LLRect floater_rect = mDockableFloater->calcScreenRect();
mGetAllowedRectCallback(rootRect);
@@ -150,13 +155,13 @@ bool LLDockControl::isDockVisible()
{
bool res = true;
- if (mDockWidget != NULL)
+ if (getDock() != NULL)
{
//we should check all hierarchy
- res = mDockWidget->isInVisibleChain();
+ res = getDock()->isInVisibleChain();
if (res)
{
- LLRect dockRect = mDockWidget->calcScreenRect();
+ LLRect dockRect = getDock()->calcScreenRect();
switch (mDockAt)
{
@@ -169,7 +174,7 @@ bool LLDockControl::isDockVisible()
// assume that parent for all dockable floaters
// is the root view
LLRect dockParentRect =
- mDockWidget->getRootView()->calcScreenRect();
+ getDock()->getRootView()->calcScreenRect();
if (dockRect.mRight <= dockParentRect.mLeft
|| dockRect.mLeft >= dockParentRect.mRight)
{
@@ -189,7 +194,7 @@ bool LLDockControl::isDockVisible()
void LLDockControl::moveDockable()
{
// calculate new dockable position
- LLRect dockRect = mDockWidget->calcScreenRect();
+ LLRect dockRect = getDock()->calcScreenRect();
LLRect rootRect;
mGetAllowedRectCallback(rootRect);
@@ -263,7 +268,7 @@ void LLDockControl::moveDockable()
// calculate dock tongue position
- dockParentRect = mDockWidget->getParent()->calcScreenRect();
+ dockParentRect = getDock()->getParent()->calcScreenRect();
if (dockRect.getCenterX() < dockParentRect.mLeft)
{
mDockTongueX = dockParentRect.mLeft - mDockTongue->getWidth() / 2;
@@ -299,7 +304,7 @@ void LLDockControl::moveDockable()
}
// calculate dock tongue position
- dockParentRect = mDockWidget->getParent()->calcScreenRect();
+ dockParentRect = getDock()->getParent()->calcScreenRect();
if (dockRect.getCenterX() < dockParentRect.mLeft)
{
mDockTongueX = dockParentRect.mLeft - mDockTongue->getWidth() / 2;
diff --git a/indra/llui/lldockcontrol.h b/indra/llui/lldockcontrol.h
index c9602011f6..98a9c7236d 100644
--- a/indra/llui/lldockcontrol.h
+++ b/indra/llui/lldockcontrol.h
@@ -63,7 +63,7 @@ public:
void setDock(LLView* dockWidget);
LLView* getDock()
{
- return mDockWidget;
+ return mDockWidgetHandle.get();
}
void repositionDockable();
void drawToungue();
@@ -83,7 +83,7 @@ private:
bool mRecalculateDockablePosition;
bool mDockWidgetVisible;
DocAt mDockAt;
- LLView* mDockWidget;
+ LLHandle<LLView> mDockWidgetHandle;
LLRect mPrevDockRect;
LLRect mRootRect;
LLRect mFloaterRect;
diff --git a/indra/llui/llflashtimer.cpp b/indra/llui/llflashtimer.cpp
new file mode 100644
index 0000000000..e49628acd5
--- /dev/null
+++ b/indra/llui/llflashtimer.cpp
@@ -0,0 +1,100 @@
+/**
+ * @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 "../newview/llviewerprecompiledheaders.h"
+
+#include "llflashtimer.h"
+#include "../newview/llviewercontrol.h"
+#include "lleventtimer.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 : gSavedSettings.getS32("FlashCount"));
+ if (mPeriod <= 0)
+ {
+ mPeriod = gSavedSettings.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
new file mode 100644
index 0000000000..c60f99a8ea
--- /dev/null
+++ b/indra/llui/llflashtimer.h
@@ -0,0 +1,73 @@
+/**
+ * @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"
+
+class LLFlashTimer : public LLEventTimer
+{
+public:
+
+ typedef boost::function<void (bool)> 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/llfloater.cpp b/indra/llui/llfloater.cpp
index 1594be2512..27dd7f5b32 100644
--- a/indra/llui/llfloater.cpp
+++ b/indra/llui/llfloater.cpp
@@ -64,6 +64,8 @@
// use this to control "jumping" behavior when Ctrl-Tabbing
const S32 TABBED_FLOATER_OFFSET = 0;
+extern LLControlGroup gSavedSettings;
+
namespace LLInitParam
{
void TypeValues<LLFloaterEnums::EOpenPositioning>::declareValues()
@@ -627,6 +629,17 @@ void LLFloater::setVisible( BOOL visible )
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::handleVisibilityChange ( BOOL new_visibility )
{
@@ -642,14 +655,20 @@ void LLFloater::openFloater(const LLSD& key)
{
llinfos << "Opening floater " << getName() << llendl;
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");
+ //Don't play a sound for incoming voice call based upon chat preference setting
+ bool playSound = !(getName() == "incoming call" && gSavedSettings.getBOOL("PlaySoundIncomingVoiceCall") == FALSE);
+
+ if(playSound)
+ {
+ make_ui_sound("UISndWindowOpen");
+ }
}
//RN: for now, we don't allow rehosting from one multifloater to another
@@ -713,6 +732,33 @@ void LLFloater::closeFloater(bool 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
for(handle_set_iter_t dependent_it = mDependents.begin();
dependent_it != mDependents.end(); )
@@ -731,28 +777,6 @@ void LLFloater::closeFloater(bool app_quitting)
}
cleanupHandles();
- 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);
- }
- }
-
- // STORM-1879: since this floater has focus, treat the closeFloater- call
- // like a click on the close-button, and close gear- and contextmenus
- LLMenuGL::sMenuContainer->hideMenus();
- }
dirtyRect();
@@ -789,6 +813,20 @@ void LLFloater::closeFloater(bool app_quitting)
}
/*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);
@@ -1188,7 +1226,6 @@ void LLFloater::setMinimized(BOOL minimize)
{
// minimized flag should be turned on before release focus
mMinimized = TRUE;
-
mExpandedRect = getRect();
// If the floater has been dragged while minimized in the
@@ -1261,7 +1298,6 @@ void LLFloater::setMinimized(BOOL minimize)
}
setOrigin( mExpandedRect.mLeft, mExpandedRect.mBottom );
-
if (mButtonsEnabled[BUTTON_RESTORE])
{
mButtonsEnabled[BUTTON_MINIMIZE] = TRUE;
@@ -1297,7 +1333,6 @@ void LLFloater::setMinimized(BOOL minimize)
// Reshape *after* setting mMinimized
reshape( mExpandedRect.getWidth(), mExpandedRect.getHeight(), TRUE );
- applyPositioning(NULL, false);
}
make_ui_sound("UISndWindowClose");
@@ -1419,7 +1454,6 @@ void LLFloater::setHost(LLMultiFloater* host)
mButtonScale = 1.f;
//mButtonsEnabled[BUTTON_TEAR_OFF] = FALSE;
}
- updateTitleButtons();
if (host)
{
mHostHandle = host->getHandle();
@@ -1429,6 +1463,8 @@ void LLFloater::setHost(LLMultiFloater* host)
{
mHostHandle.markDead();
}
+
+ updateTitleButtons();
}
void LLFloater::moveResizeHandlesToFront()
@@ -1587,8 +1623,17 @@ void LLFloater::bringToFront( S32 x, S32 y )
// virtual
void LLFloater::setVisibleAndFrontmost(BOOL take_focus)
{
- setVisible(TRUE);
- setFrontmost(take_focus);
+ LLMultiFloater* hostp = getHost();
+ if (hostp)
+ {
+ hostp->setVisible(TRUE);
+ hostp->setFrontmost(take_focus);
+ }
+ else
+ {
+ setVisible(TRUE);
+ setFrontmost(take_focus);
+ }
}
void LLFloater::setFrontmost(BOOL take_focus)
@@ -1670,10 +1715,12 @@ void LLFloater::onClickTearOff(LLFloater* self)
gFloaterView->addChild(self);
self->openFloater(self->getKey());
-
- // only force position for floaters that don't have that data saved
- if (self->mRectControl.empty())
+ 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);
}
@@ -1687,6 +1734,10 @@ void LLFloater::onClickTearOff(LLFloater* self)
LLMultiFloater* new_host = (LLMultiFloater*)self->mLastHostHandle.get();
if (new_host)
{
+ if (self->mSaveRect)
+ {
+ self->storeRectControl();
+ }
self->setMinimized(FALSE); // to reenable minimize button if it was minimized
new_host->showFloater(self);
// make sure host is visible
@@ -1695,6 +1746,7 @@ void LLFloater::onClickTearOff(LLFloater* self)
self->setTornOff(false);
}
self->updateTitleButtons();
+ self->setOpenPositioning(LLFloaterEnums::POSITIONING_RELATIVE);
}
// static
@@ -1720,6 +1772,18 @@ void LLFloater::onClickHelp( LLFloater* self )
}
}
+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()
{
@@ -2164,7 +2228,8 @@ LLFloaterView::LLFloaterView (const Params& p)
mFocusCycleMode(FALSE),
mMinimizePositionVOffset(0),
mSnapOffsetBottom(0),
- mSnapOffsetRight(0)
+ mSnapOffsetRight(0),
+ mFrontChild(NULL)
{
mSnapView = getHandle();
}
@@ -2313,6 +2378,17 @@ LLRect LLFloaterView::findNeighboringPosition( LLFloater* reference_floater, LLF
void LLFloaterView::bringToFront(LLFloater* child, BOOL give_focus)
{
+ if (mFrontChild == child)
+ {
+ if (give_focus && !gFocusMgr.childHasKeyboardFocus(child))
+ {
+ child->setFocus(TRUE);
+ }
+ return;
+ }
+
+ mFrontChild = child;
+
// *TODO: make this respect floater's mAutoFocus value, instead of
// using parameter
if (child->getHost())
@@ -2320,15 +2396,14 @@ void LLFloaterView::bringToFront(LLFloater* child, BOOL give_focus)
// this floater is hosted elsewhere and hence not one of our children, abort
return;
}
- std::vector<LLView*> floaters_to_move;
+ std::vector<LLFloater*> floaters_to_move;
// Look at all floaters...tab
- for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ for (child_list_const_iter_t child_it = beginChild(); child_it != endChild(); ++child_it)
{
- LLView* viewp = *child_it;
- LLFloater *floater = (LLFloater *)viewp;
+ LLFloater* floater = dynamic_cast<LLFloater*>(*child_it);
// ...but if I'm a dependent floater...
- if (child->isDependent())
+ 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());
@@ -2336,15 +2411,14 @@ void LLFloaterView::bringToFront(LLFloater* child, BOOL give_focus)
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(); )
+ 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);
}
- ++dependent_it;
}
//...before bringing my parent to the front...
floaters_to_move.push_back(floater);
@@ -2352,10 +2426,10 @@ void LLFloaterView::bringToFront(LLFloater* child, BOOL give_focus)
}
}
- std::vector<LLView*>::iterator view_it;
- for(view_it = floaters_to_move.begin(); view_it != floaters_to_move.end(); ++view_it)
+ std::vector<LLFloater*>::iterator floater_it;
+ for(floater_it = floaters_to_move.begin(); floater_it != floaters_to_move.end(); ++floater_it)
{
- LLFloater* floaterp = (LLFloater*)(*view_it);
+ LLFloater* floaterp = *floater_it;
sendChildToFront(floaterp);
// always unminimize dependee, but allow dependents to stay minimized
@@ -2367,23 +2441,19 @@ void LLFloaterView::bringToFront(LLFloater* child, BOOL give_focus)
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(); )
+ 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);
- //don't un-minimize dependent windows automatically
- // respect user's wishes
- //dependent->setMinimized(FALSE);
}
- ++dependent_it;
}
// ...and finally bringing myself to front
// (do this last, so that I'm left in front at end of this call)
- if( *getChildList()->begin() != child )
+ if (*beginChild() != child)
{
sendChildToFront(child);
}
@@ -2923,21 +2993,14 @@ void LLFloaterView::popVisibleAll(const skip_list_t& skip_list)
void LLFloater::setInstanceName(const std::string& name)
{
- if (name == mInstanceName)
- return;
+ if (name != mInstanceName)
+ {
llassert_always(mInstanceName.empty());
mInstanceName = name;
if (!mInstanceName.empty())
{
std::string ctrl_name = getControlName(mInstanceName, mKey);
-
- // save_rect and save_visibility only apply to registered floaters
- if (mSaveRect)
- {
- mRectControl = LLFloaterReg::declareRectControl(ctrl_name);
- mPosXControl = LLFloaterReg::declarePosXControl(ctrl_name);
- mPosYControl = LLFloaterReg::declarePosYControl(ctrl_name);
- }
+ initRectControl();
if (!mVisibilityControl.empty())
{
mVisibilityControl = LLFloaterReg::declareVisibilityControl(ctrl_name);
@@ -2948,6 +3011,7 @@ void LLFloater::setInstanceName(const std::string& name)
}
}
}
+}
void LLFloater::setKey(const LLSD& newkey)
{
diff --git a/indra/llui/llfloater.h b/indra/llui/llfloater.h
index aef63bcf93..cb5bf28db3 100644
--- a/indra/llui/llfloater.h
+++ b/indra/llui/llfloater.h
@@ -217,13 +217,17 @@ public:
/*virtual*/ void setFocus( BOOL b );
/*virtual*/ void setIsChrome(BOOL is_chrome);
/*virtual*/ void setRect(const LLRect &rect);
+ void setIsSingleInstance(BOOL is_single_instance);
void initFloater(const Params& p);
void openFloater(const LLSD& key = LLSD());
// If allowed, close the floater cleanly, releasing focus.
- void closeFloater(bool app_quitting = false);
+ 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);
@@ -301,6 +305,7 @@ public:
/*virtual*/ void handleVisibilityChange ( BOOL new_visibility ); // do not override
void setFrontmost(BOOL take_focus = TRUE);
+ virtual void setVisibleAndFrontmost(BOOL take_focus=TRUE);
// Defaults to false.
virtual BOOL canSaveAs() const { return FALSE; }
@@ -324,6 +329,8 @@ public:
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
@@ -354,6 +361,7 @@ protected:
void stackWith(LLFloater& other);
+ virtual void initRectControl();
virtual bool applyRectControl();
bool applyDockState();
void applyPositioning(LLFloater* other, bool on_open);
@@ -367,7 +375,6 @@ protected:
void setInstanceName(const std::string& name);
virtual void bringToFront(S32 x, S32 y);
- virtual void setVisibleAndFrontmost(BOOL take_focus=TRUE);
void setExpandedRect(const LLRect& rect) { mExpandedRect = rect; } // size when not minimized
const LLRect& getExpandedRect() const { return mExpandedRect; }
@@ -441,9 +448,10 @@ private:
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
- std::string mInstanceName; // Store the instance name so we can remove ourselves from the list
+ 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;
@@ -570,6 +578,7 @@ private:
S32 mMinimizePositionVOffset;
typedef std::vector<std::pair<LLHandle<LLFloater>, boost::signals2::connection> > hidden_floaters_t;
hidden_floaters_t mHiddenFloaters;
+ LLFloater * mFrontChild;
};
//
diff --git a/indra/llui/llfloaterreg.cpp b/indra/llui/llfloaterreg.cpp
index 306caf2b91..c20d863612 100644
--- a/indra/llui/llfloaterreg.cpp
+++ b/indra/llui/llfloaterreg.cpp
@@ -264,17 +264,9 @@ bool LLFloaterReg::hideInstance(const std::string& name, const LLSD& key)
LLFloater* instance = findInstance(name, key);
if (instance)
{
- // When toggling *visibility*, close the host instead of the floater when hosted
- if (instance->getHost())
- instance->getHost()->closeFloater();
- else
- instance->closeFloater();
- return true;
- }
- else
- {
- return false;
+ instance->closeHostedFloater();
}
+ return (instance != NULL);
}
//static
@@ -284,11 +276,7 @@ bool LLFloaterReg::toggleInstance(const std::string& name, const LLSD& key)
LLFloater* instance = findInstance(name, key);
if (LLFloater::isShown(instance))
{
- // When toggling *visibility*, close the host instead of the floater when hosted
- if (instance->getHost())
- instance->getHost()->closeFloater();
- else
- instance->closeFloater();
+ instance->closeHostedFloater();
return false;
}
else
@@ -481,31 +469,58 @@ void LLFloaterReg::toggleInstanceOrBringToFront(const LLSD& sdname, const LLSD&
// * 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)
{
lldebugs << "Unable to get instance of floater '" << name << "'" << llendl;
+ return;
}
- else if (instance->isMinimized())
+
+ // If hosted, we need to take that into account
+ LLFloater* host = instance->getHost();
+
+ if (host)
{
- instance->setMinimized(FALSE);
- instance->setVisibleAndFrontmost();
- }
- else if (!instance->isShown())
- {
- instance->openFloater(key);
- instance->setVisibleAndFrontmost();
- }
- else if (!instance->isFrontmost())
- {
- instance->setVisibleAndFrontmost();
+ if (host->isMinimized() || !host->isShown() || !host->isFrontmost())
+ {
+ host->setMinimized(FALSE);
+ instance->openFloater(key);
+ instance->setVisibleAndFrontmost();
+ }
+ else if (!instance->getVisible())
+ {
+ instance->openFloater(key);
+ instance->setVisibleAndFrontmost();
+ instance->setFocus(TRUE);
+ }
+ else
+ {
+ instance->closeHostedFloater();
+ }
}
else
{
- instance->closeFloater();
+ if (instance->isMinimized())
+ {
+ instance->setMinimized(FALSE);
+ instance->setVisibleAndFrontmost();
+ }
+ else if (!instance->isShown())
+ {
+ instance->openFloater(key);
+ instance->setVisibleAndFrontmost();
+ }
+ else if (!instance->isFrontmost())
+ {
+ instance->setVisibleAndFrontmost();
+ }
+ else
+ {
+ instance->closeHostedFloater();
+ }
}
}
diff --git a/indra/llui/llfolderview.cpp b/indra/llui/llfolderview.cpp
new file mode 100644
index 0000000000..8feaf654f0
--- /dev/null
+++ b/indra/llui/llfolderview.cpp
@@ -0,0 +1,1954 @@
+/**
+ * @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 <algorithm>
+
+///----------------------------------------------------------------------------
+/// Local function declarations, constants, enums, and typedefs
+///----------------------------------------------------------------------------
+
+const S32 RENAME_WIDTH_PAD = 4;
+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)
+{ }
+
+///----------------------------------------------------------------------------
+/// Class LLFolderViewScrollContainer
+///----------------------------------------------------------------------------
+
+// virtual
+const LLRect LLFolderViewScrollContainer::getScrolledViewRect() const
+{
+ LLRect rect = LLRect::null;
+ if (mScrolledView)
+ {
+ LLFolderView* folder_view = dynamic_cast<LLFolderView*>(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),
+ show_empty_message("show_empty_message", true),
+ use_ellipses("use_ellipses", false),
+ options_menu("options_menu", "")
+{
+ folder_indentation = -4;
+}
+
+
+// Default constructor
+LLFolderView::LLFolderView(const Params& p)
+: LLFolderViewFolder(p),
+ mScrollContainer( NULL ),
+ mPopupMenuHandle(),
+ mAllowMultiSelect(p.allow_multiselect),
+ mShowEmptyMessage(p.show_empty_message),
+ mShowFolderHierarchy(FALSE),
+ mRenameItem( NULL ),
+ mNeedsScroll( FALSE ),
+ mUseLabelSuffix(p.use_label_suffix),
+ mPinningSelectedItem(FALSE),
+ mNeedsAutoSelect( FALSE ),
+ mAutoSelectOverride(FALSE),
+ mNeedsAutoRename(FALSE),
+ mShowSelectionContext(FALSE),
+ mShowSingleSelection(FALSE),
+ mArrangeGeneration(0),
+ mSignalSelectCallback(0),
+ mMinWidth(0),
+ mDragAndDropThisFrame(FALSE),
+ mCallbackRegistrar(NULL),
+ mParentPanel(p.parent_panel),
+ mUseEllipses(p.use_ellipses),
+ mDraggingOverItem(NULL),
+ mStatusTextBox(NULL),
+ mShowItemLinkOverlays(p.show_item_link_overlays),
+ mViewModel(p.view_model)
+{
+ 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 = p.folder_indentation;
+
+ //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.prevalidate_callback(&LLTextValidate::validateASCIIPrintableNoPipe);
+ params.commit_on_focus_lost(true);
+ params.visible(false);
+ mRenamer = LLUICtrlFactory::create<LLLineEditor> (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<LLTextBox> (text_p);
+ mStatusTextBox->setFollowsLeft();
+ mStatusTextBox->setFollowsTop();
+ //addChild(mStatusTextBox);
+
+
+ // make the popup menu available
+ LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>(p.options_menu, LLMenuGL::sMenuContainer, LLMenuHolderGL::child_registry_t::instance());
+ if (!menu)
+ {
+ menu = LLUICtrlFactory::getDefaultWidget<LLMenuGL>("inventory_menu");
+ }
+ menu->setBackgroundColor(LLUIColorTable::instance().getColor("MenuPopupBgColor"));
+ mPopupMenuHandle = menu->getHandle();
+
+ mViewModelItem->openItem();
+}
+
+// Destroys the object
+LLFolderView::~LLFolderView( void )
+{
+ closeRenamer();
+
+ // The release focus call can potentially call the
+ // scrollcontainer, which can potentially be called with a partly
+ // destroyed scollcontainer. Just null it out here, and no worries
+ // about calling into the invalid scroll container.
+ // Same with the renamer.
+ mScrollContainer = NULL;
+ mRenameItem = NULL;
+ mRenamer = NULL;
+ mStatusTextBox = NULL;
+
+ mAutoOpenItems.removeAllNodes();
+
+ if (mPopupMenuHandle.get()) mPopupMenuHandle.get()->die();
+
+ mAutoOpenItems.removeAllNodes();
+ clearSelection();
+ mItems.clear();
+ mFolders.clear();
+
+ 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), llround(mCurHeight) );
+
+ LLRect new_scroll_rect = (mScrollContainer ? mScrollContainer->getContentWindowRect() : LLRect());
+ if (new_scroll_rect.getWidth() != scroll_rect.getWidth())
+ {
+ reshape( llmax(scroll_rect.getWidth(), mMinWidth), llround(mCurHeight) );
+ }
+
+ // move item renamer text field to item's new position
+ updateRenamerPosition();
+
+ return llround(mTargetHeight);
+}
+
+static LLFastTimer::DeclareTimer FTM_FILTER("Filter Folder View");
+
+void LLFolderView::filter( LLFolderViewFilter& filter )
+{
+ LLFastTimer t2(FTM_FILTER);
+ filter.setFilterCount(llclamp(LLUI::sSettingGroups["config"]->getS32("FilterItemsPerFrame"), 1, 5000));
+
+ 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(llround(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->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;
+}
+
+static LLFastTimer::DeclareTimer FTM_SANITIZE_SELECTION("Sanitize Selection");
+void LLFolderView::sanitizeSelection()
+{
+ LLFastTimer _(FTM_SANITIZE_SELECTION);
+ // store off current item in case it is automatically deselected
+ // and we want to preserve context
+ LLFolderViewItem* original_selected_item = getCurSelectedItem();
+
+ std::vector<LLFolderViewItem*> 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<LLFolderViewItem*>::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();
+}
+
+std::set<LLFolderViewItem*> LLFolderView::getSelectionList() const
+{
+ std::set<LLFolderViewItem*> selection;
+ std::copy(mSelectedItems.begin(), mSelectedItems.end(), std::inserter(selection, selection.begin()));
+ return selection;
+}
+
+bool LLFolderView::startDrag()
+{
+ std::vector<LLFolderViewModelItem*> 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();
+}
+
+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();
+ }
+
+ if (mSearchTimer.getElapsedTimeF32() > LLUI::sSettingGroups["config"]->getF32("TypeAheadTimeout") || !mSearchString.size())
+ {
+ mSearchString.clear();
+ }
+
+ if (hasVisibleChildren())
+ {
+ mStatusTextBox->setVisible( FALSE );
+ }
+ else if (mShowEmptyMessage)
+ {
+ mStatusTextBox->setValue(getFolderViewModel()->getStatusText());
+ 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);
+ }
+ }
+
+ // 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();
+
+ // 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::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<LLFolderViewItem*> 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
+ {
+ llinfos << "Cannot delete " << item->getName() << llendl;
+ 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->hasFocus());
+ }
+ }
+ arrangeAll();
+ }
+ else if (count > 1)
+ {
+ LLDynamicArray<LLFolderViewModelItem*> listeners;
+ LLFolderViewModelItem* listener;
+
+ setSelection(item_to_select, item_to_select ? item_to_select->isOpen() : false, mParentPanel->hasFocus());
+
+ for(S32 i = 0; i < count; ++i)
+ {
+ listener = items[i]->getViewModelItem();
+ if(listener && (listeners.find(listener) == LLDynamicArray<LLFolderViewModelItem*>::FAIL))
+ {
+ listeners.put(listener);
+ }
+ }
+ listener = static_cast<LLFolderViewModelItem*>(listeners.get(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);
+ 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<LLFolderViewItem*> inventory_selected = getSelectionList();
+
+ // Move each item to the clipboard and out of their folder
+ for (std::set<LLFolderViewItem*>::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();
+ listener->removeItem();
+ }
+ }
+
+ // Update the selection
+ setSelection(item_to_select, item_to_select ? item_to_select->isOpen() : false, mParentPanel->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<LLFolderViewFolder*> 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<LLFolderViewFolder*>(item);
+ if (folder == NULL)
+ {
+ folder = item->getParentFolder();
+ }
+ folder_set.insert(folder);
+ }
+
+ std::set<LLFolderViewFolder*>::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 )
+{
+ // 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 );
+ mRenamer->setTopLostCallback(boost::bind(&LLFolderView::onRenamerLost, this));
+ LLUI::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();
+ }
+
+ LLView *item = NULL;
+ if (getChildCount() > 0)
+ {
+ item = *(getChildList()->begin());
+ }
+
+ 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();
+
+ if (!mKeyboardSelection)
+ {
+ setSelection(last_selected, FALSE, TRUE);
+ mKeyboardSelection = TRUE;
+ }
+
+ LLFolderViewItem* next = NULL;
+ if (mask & MASK_SHIFT)
+ {
+ // don't shift select down to children of folders (they are implicitly selected through parent)
+ next = last_selected->getNextOpenNode(FALSE);
+ if (next)
+ {
+ if (next->isSelected())
+ {
+ // shrink selection
+ changeSelection(last_selected, FALSE);
+ }
+ else if (last_selected->getParentFolder() == next->getParentFolder())
+ {
+ // grow selection
+ changeSelection(next, TRUE);
+ }
+ }
+ }
+ else
+ {
+ next = last_selected->getNextOpenNode();
+ 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();
+
+ if (!mKeyboardSelection)
+ {
+ setSelection(last_selected, FALSE, TRUE);
+ mKeyboardSelection = TRUE;
+ }
+
+ LLFolderViewItem* prev = NULL;
+ if (mask & MASK_SHIFT)
+ {
+ // don't shift select down to children of folders (they are implicitly selected through parent)
+ prev = last_selected->getPreviousOpenNode(FALSE);
+ if (prev)
+ {
+ if (prev->isSelected())
+ {
+ // shrink selection
+ changeSelection(last_selected, FALSE);
+ }
+ else if (last_selected->getParentFolder() == prev->getParentFolder())
+ {
+ // grow selection
+ changeSelection(prev, TRUE);
+ }
+ }
+ }
+ else
+ {
+ prev = last_selected->getPreviousOpenNode();
+ 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();
+ 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)
+ {
+ llwarns << "LLFolderView::handleUnicodeCharHere - Don't handle non-ascii yet, aborting" << llendl;
+ return FALSE;
+ }
+
+ BOOL handled = FALSE;
+ if (mParentPanel->hasFocus())
+ {
+ // SL-51858: Key presses are not being passed to the Popup menu.
+ // A proper fix is non-trivial so instead just close the menu.
+ LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandle.get();
+ if (menu && menu->isOpen())
+ {
+ LLMenuGL::sMenuContainer->hideMenus();
+ }
+
+ //do text search
+ if (mSearchTimer.getElapsedTimeF32() > LLUI::sSettingGroups["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->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;
+ }
+ }
+
+ const std::string current_item_label(search_item->getViewModelItem()->getSearchableName());
+ 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->setFocus(TRUE);
+
+ BOOL handled = childrenHandleRightMouseDown(x, y, mask) != NULL;
+ S32 count = mSelectedItems.size();
+ LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandle.get();
+ if ( handled
+ && ( count > 0 && (hasVisibleChildren()) ) // show menu only if selected items are visible
+ && menu )
+ {
+ if (mCallbackRegistrar)
+ {
+ mCallbackRegistrar->pushScope();
+ }
+
+ updateMenuOptions(menu);
+
+ menu->updateParent(LLMenuGL::sMenuContainer);
+ LLMenuGL::showPopup(this, menu, x, y);
+ 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 );
+}
+
+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()
+{
+ closeRenamer();
+ if (mPopupMenuHandle.get()) mPopupMenuHandle.get()->die();
+ mPopupMenuHandle = LLHandle<LLView>();
+ 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 LLFastTimer::DeclareTimer FTM_AUTO_SELECT("Open and Select");
+static LLFastTimer::DeclareTimer 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.
+ LLFastTimer t2(FTM_INVENTORY);
+
+ if (getFolderViewModel()->getFilter().isModified() && getFolderViewModel()->getFilter().isNotDefault())
+ {
+ mNeedsAutoSelect = TRUE;
+ }
+ // filter to determine visibility before arranging
+ filter(getFolderViewModel()->getFilter());
+ // Clear the modified setting on the filter only if the filter count is non-zero after running the filter process
+ // Note: if the filter count is zero, then the filter most likely halted before completing the entire set of items
+ if (getFolderViewModel()->getFilter().isModified() && (getFolderViewModel()->getFilter().getFilterCount() > 0))
+ {
+ getFolderViewModel()->getFilter().clearModified();
+ }
+
+ // automatically show matching items, and select first one if we had a selection
+ if (mNeedsAutoSelect)
+ {
+ LLFastTimer t3(FTM_AUTO_SELECT);
+ // select new item only if a filtered item not currently selected
+ LLFolderViewItem* selected_itemp = mSelectedItems.empty() ? NULL : mSelectedItems.back();
+ if (!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 (getFolderViewModel()->getFilter().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 = getViewModelItem()->passedFilter()
+ && mViewModel->contentsReady();
+ if (filter_finished
+ || gFocusMgr.childHasKeyboardFocus(mParentPanel)
+ || gFocusMgr.childHasMouseCapture(mParentPanel))
+ {
+ // 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();
+
+ //Puts folders/items in proper positions
+ 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)
+ {
+ scrollToShowItem(mSelectedItems.back(), constraint_rect);
+ // continue scrolling until animated layout change is done
+ if (filter_finished
+ && (!needsArrange() || !is_visible))
+ {
+ mNeedsScroll = FALSE;
+ }
+ }
+
+ 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;
+}
+
+void LLFolderView::dumpSelectionInformation()
+{
+ llinfos << "LLFolderView::dumpSelectionInformation()" << llendl;
+ llinfos << "****************************************" << llendl;
+ selected_items_t::iterator item_it;
+ for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it)
+ {
+ llinfos << " " << (*item_it)->getName() << llendl;
+ }
+ llinfos << "****************************************" << llendl;
+}
+
+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::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;
+ }
+
+ addNoOptions(menu);
+}
+
+// Refresh the context menu (that is already shown).
+void LLFolderView::updateMenu()
+{
+ LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandle.get();
+ if (menu && menu->getVisible())
+ {
+ updateMenuOptions(menu);
+ menu->needsArrange(); // update menu height if needed
+ }
+}
+
+bool LLFolderView::selectFirstItem()
+{
+ for (folders_t::iterator iter = mFolders.begin();
+ iter != mFolders.end();++iter)
+ {
+ LLFolderViewFolder* folder = (*iter );
+ if (folder->getVisible())
+ {
+ LLFolderViewItem* itemp = folder->getNextFromChild(0,true);
+ if(itemp)
+ setSelection(itemp,FALSE,TRUE);
+ return true;
+ }
+
+ }
+ for(items_t::iterator iit = mItems.begin();
+ iit != mItems.end(); ++iit)
+ {
+ LLFolderViewItem* itemp = (*iit);
+ if (itemp->getVisible())
+ {
+ setSelection(itemp,FALSE,TRUE);
+ return true;
+ }
+ }
+ return false;
+}
+bool LLFolderView::selectLastItem()
+{
+ for(items_t::reverse_iterator iit = mItems.rbegin();
+ iit != mItems.rend(); ++iit)
+ {
+ LLFolderViewItem* itemp = (*iit);
+ if (itemp->getVisible())
+ {
+ setSelection(itemp,FALSE,TRUE);
+ return true;
+ }
+ }
+ for (folders_t::reverse_iterator iter = mFolders.rbegin();
+ iter != mFolders.rend();++iter)
+ {
+ LLFolderViewFolder* folder = (*iter);
+ if (folder->getVisible())
+ {
+ LLFolderViewItem* itemp = folder->getPreviousFromChild(0,true);
+ if(itemp)
+ setSelection(itemp,FALSE,TRUE);
+ return true;
+ }
+ }
+ return false;
+}
+
+
+S32 LLFolderView::notify(const LLSD& info)
+{
+ if(info.has("action"))
+ {
+ std::string str_action = info["action"];
+ if(str_action == "select_first")
+ {
+ setFocus(true);
+ selectFirstItem();
+ scrollToShowSelection();
+ return 1;
+
+ }
+ else if(str_action == "select_last")
+ {
+ setFocus(true);
+ selectLastItem();
+ scrollToShowSelection();
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+///----------------------------------------------------------------------------
+/// Local function definitions
+///----------------------------------------------------------------------------
+
+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()
+{
+ 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
new file mode 100644
index 0000000000..11fccdace4
--- /dev/null
+++ b/indra/llui/llfolderview.h
@@ -0,0 +1,399 @@
+/**
+ * @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 "stdenums.h"
+#include "lldepthstack.h"
+#include "lleditmenuhandler.h"
+#include "llfontgl.h"
+#include "llscrollcontainer.h"
+
+class LLFolderViewModelInterface;
+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<Params, LLFolderViewFolder::Params>
+ {
+ Mandatory<LLPanel*> parent_panel;
+ Optional<std::string> title;
+ Optional<bool> use_label_suffix,
+ allow_multiselect,
+ show_empty_message,
+ use_ellipses,
+ show_item_link_overlays;
+ Mandatory<LLFolderViewModelInterface*> view_model;
+ Mandatory<std::string> options_menu;
+
+
+ Params();
+ };
+
+ friend class LLFolderViewScrollContainer;
+ typedef std::deque<LLFolderViewItem*> 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; }
+
+ typedef boost::signals2::signal<void (const std::deque<LLFolderViewItem*>& 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; }
+
+ // 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();
+
+ void arrangeAll() { mArrangeGeneration++; }
+ S32 getArrangeGeneration() { return mArrangeGeneration; }
+
+ // applies filters to control visibility of items
+ virtual void filter( LLFolderViewFilter& filter);
+
+ // 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<LLFolderViewItem*> 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 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; }
+
+ LLPanel* getParentPanel() { return mParentPanel; }
+ // DEBUG only
+ void dumpSelectionInformation();
+
+ virtual S32 notify(const LLSD& info) ;
+
+ bool useLabelSuffix() { return mUseLabelSuffix; }
+ void updateMenu();
+
+private:
+ void updateMenuOptions(LLMenuGL* menu);
+ void updateRenamerPosition();
+
+protected:
+ LLScrollContainer* mScrollContainer; // NULL if this is not a child of a scroll container.
+
+ void commitRename( const LLSD& data );
+ void onRenamerLost();
+
+ void finishRenamingItem( void );
+ void closeRenamer( void );
+
+ bool selectFirstItem();
+ bool selectLastItem();
+
+ BOOL addNoOptions(LLMenuGL* menu) const;
+
+
+protected:
+ LLHandle<LLView> mPopupMenuHandle;
+
+ selected_items_t mSelectedItems;
+ BOOL mKeyboardSelection;
+ BOOL mAllowMultiSelect;
+ BOOL mShowEmptyMessage;
+ BOOL mShowFolderHierarchy;
+
+ // Renaming variables and methods
+ LLFolderViewItem* mRenameItem; // The item currently being renamed
+ LLLineEditor* mRenamer;
+
+ BOOL mNeedsScroll;
+ BOOL mPinningSelectedItem;
+ LLRect mScrollConstraintRect;
+ BOOL mNeedsAutoSelect;
+ BOOL mAutoSelectOverride;
+ BOOL mNeedsAutoRename;
+ bool mUseLabelSuffix;
+ bool mShowItemLinkOverlays;
+
+ LLDepthStack<LLFolderViewFolder> mAutoOpenItems;
+ LLFolderViewFolder* mAutoOpenCandidate;
+ LLFrameTimer mAutoOpenTimer;
+ LLFrameTimer mSearchTimer;
+ std::string mSearchString;
+ BOOL mShowSelectionContext;
+ BOOL mShowSingleSelection;
+ LLFrameTimer mMultiSelectionFadeTimer;
+ S32 mArrangeGeneration;
+
+ signal_t mSelectSignal;
+ signal_t mReshapeSignal;
+ S32 mSignalSelectCallback;
+ S32 mMinWidth;
+ BOOL mDragAndDropThisFrame;
+
+ LLPanel* mParentPanel;
+
+ LLFolderViewModelInterface* mViewModel;
+
+ /**
+ * 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;
+
+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<LLUUID> mOpenFolders;
+ BOOL mApply;
+};
+
+class LLOpenFoldersWithSelection : public LLFolderViewFunctor
+{
+public:
+ LLOpenFoldersWithSelection() {}
+ virtual ~LLOpenFoldersWithSelection() {}
+ virtual void doFolder(LLFolderViewFolder* folder);
+ virtual void doItem(LLFolderViewItem* item);
+};
+
+// 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
new file mode 100755
index 0000000000..fdb4108afb
--- /dev/null
+++ b/indra/llui/llfolderviewitem.cpp
@@ -0,0 +1,2095 @@
+/**
+* @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 "llcriticaldamp.h"
+#include "llclipboard.h"
+#include "llfocusmgr.h" // gFocusMgr
+#include "lltrans.h"
+#include "llwindow.h"
+
+///----------------------------------------------------------------------------
+/// Class LLFolderViewItem
+///----------------------------------------------------------------------------
+
+static LLDefaultChildRegistry::Register<LLFolderViewItem> r("folder_view_item");
+
+// statics
+std::map<U8, LLFontGL*> 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_open("allow_open", 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),
+ 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),
+ mLabelPaddingRight(DEFAULT_LABEL_PADDING_RIGHT),
+ mParentFolder( NULL ),
+ mIsSelected( FALSE ),
+ mIsCurSelection( FALSE ),
+ mSelectPending(FALSE),
+ 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),
+ mAllowOpen(p.allow_open),
+ 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),
+ mMaxFolderItemOverlap(p.max_folder_item_overlap)
+{
+ 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("InventoryItemColor", DEFAULT_WHITE);
+ sSearchStatusColor = LLUIColorTable::instance().getColor("InventorySearchStatusColor", DEFAULT_WHITE);
+ sColorSetInitialized = true;
+ }
+
+ if (mViewModelItem)
+ {
+ mViewModelItem->setFolderViewItem(this);
+ }
+}
+
+// Destroys the object
+LLFolderViewItem::~LLFolderViewItem()
+{
+ mViewModelItem = NULL;
+}
+
+BOOL LLFolderViewItem::postBuild()
+{
+ refresh();
+ 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);
+}
+
+void LLFolderViewItem::refresh()
+{
+ LLFolderViewModelItem& vmi = *getViewModelItem();
+
+ mLabel = vmi.getDisplayName();
+
+ setToolTip(vmi.getName());
+ mIcon = vmi.getIcon();
+ mIconOpen = vmi.getIconOpen();
+ mIconOverlay = vmi.getIconOverlay();
+
+ if (mRoot->useLabelSuffix())
+ {
+ mLabelStyle = vmi.getLabelStyle();
+ mLabelSuffix = vmi.getLabelSuffix();
+ }
+
+ mLabelWidthDirty = true;
+ vmi.dirtyFilter();
+}
+
+// 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*> LLFolderViewItem::getSelectionList() const
+{
+ std::set<LLFolderViewItem*> selection;
+ return selection;
+}
+
+// addToFolder() returns TRUE if it succeeds. FALSE otherwise
+void LLFolderViewItem::addToFolder(LLFolderViewFolder* folder)
+{
+ folder->addItem(this);
+}
+
+
+// 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)
+ {
+ mLabelWidth = getLabelXPos() + getLabelFontForStyle(mLabelStyle)->getWidth(mLabel) + getLabelFontForStyle(mLabelStyle)->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()
+{
+ 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 == FALSE)
+ {
+ 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 (mAllowOpen)
+ {
+ 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 )
+{
+ static LLCachedControl<S32> drag_and_drop_threshold(*LLUI::sSettingGroups["config"],"DragAndDropDistanceThreshold");
+
+ mIsMouseOverTitle = (y > (getRect().getHeight() - mItemHeight));
+
+ if( hasMouseCapture() && isMovable() )
+ {
+ LLFolderView* root = getRoot();
+
+ if( (x - mDragStartX) * (x - mDragStartX) + (y - mDragStartY) * (y - mDragStartY) > drag_and_drop_threshold() * drag_and_drop_threshold()
+ && 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);
+ }
+
+ return TRUE;
+ }
+ else
+ {
+ getRoot()->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;
+}
+
+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)
+ {
+ lldebugst(LLERR_USER_INPUT) << "dragAndDrop handled by LLFolderViewItem" << llendl;
+ }
+
+ 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() || getViewModelItem()->hasChildren())
+ {
+ 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;
+}
+
+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, 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<LLFolderViewItem>();
+ const S32 TOP_PAD = default_params.item_top_pad;
+
+ const LLFontGL* font = getLabelFontForStyle(mLabelStyle);
+
+ getViewModelItem()->update();
+
+ 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;
+
+ if (filter_string_length > 0)
+ {
+ S32 left = llround(text_left) + font->getWidth(combined_string, 0, mViewModelItem->getFilterStringOffset()) - 2;
+ S32 right = left + font->getWidth(combined_string, mViewModelItem->getFilterStringOffset(), filter_string_length) + 2;
+ S32 bottom = llfloor(getRect().getHeight() - font->getLineHeight() - 3 - TOP_PAD);
+ S32 top = getRect().getHeight() - TOP_PAD;
+
+ 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;
+ drawLabel(font, text_left, y, color, right_x);
+
+ //--------------------------------------------------------------------------------//
+ // Draw label suffix
+ //
+ if (!mLabelSuffix.empty())
+ {
+ font->renderUTF8( mLabelSuffix, 0, right_x, y, sSuffixColor,
+ LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW,
+ S32_MAX, S32_MAX, &right_x, FALSE );
+ }
+
+ //--------------------------------------------------------------------------------//
+ // Highlight string match
+ //
+ if (filter_string_length > 0)
+ {
+ F32 match_string_left = text_left + font->getWidthF32(combined_string, 0, mViewModelItem->getFilterStringOffset());
+ F32 yy = (F32)getRect().getHeight() - font->getLineHeight() - (F32)mTextPad - (F32)TOP_PAD;
+ font->renderUTF8( combined_string, mViewModelItem->getFilterStringOffset(), match_string_left, yy,
+ sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW,
+ filter_string_length, S32_MAX, &right_x, FALSE );
+ }
+
+ //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),
+ mLastArrangeGeneration( -1 ),
+ mLastCalculatedWidth(0)
+{
+}
+
+void LLFolderViewFolder::updateLabelRotation()
+{
+ if (mAutoOpenCountdown != 0.f)
+ {
+ mControlLabelRotation = mAutoOpenCountdown * -90.f;
+ }
+ else if (isOpen())
+ {
+ mControlLabelRotation = lerp(mControlLabelRotation, -90.f, LLCriticalDamp::getInterpolant(0.04f));
+ }
+ else
+ {
+ mControlLabelRotation = lerp(mControlLabelRotation, 0.f, LLCriticalDamp::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);
+}
+
+static LLFastTimer::DeclareTimer FTM_ARRANGE("Arrange");
+
+// Finds width and height of this object and its children. Also
+// makes sure that this view and its children are the right size.
+S32 LLFolderViewFolder::arrange( S32* width, S32* height )
+{
+ // sort before laying out contents
+ getRoot()->getFolderViewModel()->sort(this);
+
+ LLFastTimer t2(FTM_ARRANGE);
+
+ // evaluate mHasVisibleChildren
+ mHasVisibleChildren = false;
+ if (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->passedFilter();
+ 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->passedFilter();
+ if (found)
+ break;
+ }
+ }
+
+ mHasVisibleChildren = found;
+ }
+
+ // 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->passedFilter()); // passed filter or has descendants that passed filter
+
+ if (folderp->getVisible())
+ {
+ S32 child_width = *width;
+ S32 child_height = 0;
+ S32 child_top = parent_item_height - llround(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->passedFilter());
+
+ if (itemp->getVisible())
+ {
+ S32 child_width = *width;
+ S32 child_height = 0;
+ S32 child_top = parent_item_height - llround(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, LLCriticalDamp::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()
+ > llround(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
+ > llround(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(),llround(mCurHeight));
+
+ // pass current height value back to parent
+ *height = llround(mCurHeight);
+
+ return llround(mTargetHeight);
+}
+
+BOOL LLFolderViewFolder::needsArrange()
+{
+ return mLastArrangeGeneration < getRoot()->getArrangeGeneration();
+}
+
+// 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)
+ {
+ 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<LLFolderViewFolder*> item_a_ancestors;
+
+ LLFolderViewFolder* parent = item_a->getParentFolder();
+ while(parent)
+ {
+ item_a_ancestors.push_back(parent);
+ parent = parent->getParentFolder();
+ }
+
+ std::deque<LLFolderViewFolder*> 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<LLFolderViewItem*>& 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)
+ {
+ 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)
+ {
+ 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)
+ {
+ 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)
+ {
+ items.push_back(*it);
+ }
+
+ if (*it == start)
+ {
+ selecting = true;
+ }
+ }
+ }
+}
+
+void LLFolderViewFolder::extendSelectionTo(LLFolderViewItem* new_selection)
+{
+ if (getRoot()->getAllowMultiSelect() == FALSE) 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<LLFolderViewItem*> 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<LLFolderViewItem*> 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<LLFolderViewItem*>::reverse_iterator it = items_to_select_reverse.rbegin(), end_it = items_to_select_reverse.rend();
+ it != end_it;
+ ++it)
+ {
+ items_to_select_forward.push_back(*it);
+ }
+
+ LLFolderView* root = getRoot();
+
+ for (std::vector<LLFolderViewItem*>::iterator it = items_to_select_forward.begin(), end_it = items_to_select_forward.end();
+ it != end_it;
+ ++it)
+ {
+ LLFolderViewItem* item = *it;
+ if (item->isSelected())
+ {
+ root->removeFromSelectionList(item);
+ }
+ else
+ {
+ item->selectItem();
+ }
+ root->addToSelectionList(item);
+ }
+
+ if (new_selection->isSelected())
+ {
+ root->removeFromSelectionList(new_selection);
+ }
+ else
+ {
+ new_selection->selectItem();
+ }
+ root->addToSelectionList(new_selection);
+}
+
+
+void LLFolderViewFolder::destroyView()
+{
+ while (!mItems.empty())
+ {
+ LLFolderViewItem *itemp = mItems.back();
+ itemp->destroyView(); // LLFolderViewItem::destroyView() removes entry from mItems
+ }
+
+ while (!mFolders.empty())
+ {
+ LLFolderViewFolder *folderp = mFolders.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 )
+{
+ 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<LLFolderViewFolder*>(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
+ 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;
+}
+
+// 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()
+{
+ if ( mLastArrangeGeneration != -1)
+ {
+ 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)
+{
+ setOpenArrangeRecursively(openitem);
+}
+
+void LLFolderViewFolder::setOpenArrangeRecursively(BOOL openitem, ERecurseType recurse)
+{
+ BOOL was_open = isOpen();
+ mIsOpen = openitem;
+ if(!was_open && openitem)
+ {
+ getViewModelItem()->openItem();
+ }
+ 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);
+
+ lldebugst(LLERR_USER_INPUT) << "dragAndDrop handled by LLFolderViewFolder" << llendl;
+ }
+
+ return TRUE;
+}
+
+BOOL LLFolderViewFolder::handleDragAndDropToThisFolder(MASK mask,
+ BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ std::string& tooltip_msg)
+{
+ 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)
+ {
+ 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( isOpen() )
+ {
+ handled = childrenHandleDoubleClick( x, y, mask ) != NULL;
+ }
+ if( !handled )
+ {
+ 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
new file mode 100755
index 0000000000..ca31931e19
--- /dev/null
+++ b/indra/llui/llfolderviewitem.h
@@ -0,0 +1,458 @@
+/**
+* @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<Params, LLView::Params>
+ {
+ Optional<LLUIImage*> folder_arrow_image,
+ selection_image;
+ Mandatory<LLFolderView*> root;
+ Mandatory<LLFolderViewModelItem*> listener;
+
+ Optional<S32> folder_indentation, // pixels
+ item_height,
+ item_top_pad;
+
+ Optional<time_t> creation_date;
+ Optional<bool> allow_open;
+
+ Optional<LLUIColor> font_color;
+ Optional<LLUIColor> font_highlight_color;
+
+ Optional<S32> left_pad,
+ icon_pad,
+ icon_width,
+ text_pad,
+ text_pad_right,
+ arrow_size,
+ max_folder_item_overlap;
+ 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<LLFolderViewModelItem> mViewModelItem;
+ LLFontGL::StyleFlags mLabelStyle;
+ std::string mLabelSuffix;
+ 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,
+ mAllowOpen,
+ mSelectPending;
+
+ 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 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();
+ 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<LLFolderViewItem*> 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() { return mIsCurSelection; }
+
+ BOOL hasVisibleChildren() { return mHasVisibleChildren; }
+
+ // 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() { return mIndentation; }
+
+ virtual BOOL passedFilter(S32 filter_generation = -1);
+
+ // refresh information from the object being viewed.
+ virtual void refresh();
+
+ // 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<U8, LLFontGL*> 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<LLFolderViewItem*> items_t;
+ typedef std::list<LLFolderViewFolder*> folders_t;
+
+protected:
+ items_t mItems;
+ folders_t mFolders;
+
+ BOOL mIsOpen;
+ BOOL mExpanderHighlighted;
+ F32 mCurHeight;
+ F32 mTargetHeight;
+ F32 mAutoOpenCountdown;
+ S32 mLastArrangeGeneration;
+ S32 mLastCalculatedWidth;
+ S32 mMostFilteredDescendantGeneration;
+ bool mNeedsSort;
+
+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();
+
+ // extractItem() removes the specified item from the folder, but
+ // doesn't delete it.
+ virtual void extractItem( LLFolderViewItem* item );
+
+ // 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<LLFolderViewItem*>& 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<typename SORT_FUNC> void sortFolders(const SORT_FUNC& func) { mFolders.sort(func); }
+ template<typename SORT_FUNC> void sortItems(const SORT_FUNC& func) { mItems.sort(func); }
+};
+
+
+#endif // LLFOLDERVIEWITEM_H
diff --git a/indra/llui/llfolderviewmodel.cpp b/indra/llui/llfolderviewmodel.cpp
new file mode 100644
index 0000000000..3593804554
--- /dev/null
+++ b/indra/llui/llfolderviewmodel.cpp
@@ -0,0 +1,68 @@
+/**
+ * @file llfolderviewmodel.cpp
+ * @brief Implementation of the view model 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 "llfolderviewmodel.h"
+#include "lltrans.h"
+
+bool LLFolderViewModelCommon::needsSort(LLFolderViewModelItem* item)
+{
+ return item->getSortVersion() < mTargetSortVersion;
+}
+
+std::string LLFolderViewModelCommon::getStatusText()
+{
+ if (!contentsReady() || mFolderView->getViewModelItem()->getLastFilterGeneration() < getFilter().getCurrentGeneration())
+ {
+ return LLTrans::getString("Searching");
+ }
+ else
+ {
+ return getFilter().getEmptyLookupMessage();
+ }
+}
+
+void LLFolderViewModelCommon::filter()
+{
+ getFilter().setFilterCount(llclamp(LLUI::sSettingGroups["config"]->getS32("FilterItemsPerFrame"), 1, 5000));
+ mFolderView->getViewModelItem()->filter(getFilter());
+}
+
+bool LLFolderViewModelItemCommon::hasFilterStringMatch()
+{
+ return mStringMatchOffsetFilter != std::string::npos;
+}
+
+std::string::size_type LLFolderViewModelItemCommon::getFilterStringOffset()
+{
+ return mStringMatchOffsetFilter;
+}
+
+std::string::size_type LLFolderViewModelItemCommon::getFilterStringSize()
+{
+ return mRootViewModel.getFilter().getFilterStringSize();
+}
diff --git a/indra/llui/llfolderviewmodel.h b/indra/llui/llfolderviewmodel.h
new file mode 100644
index 0000000000..1b61212c0e
--- /dev/null
+++ b/indra/llui/llfolderviewmodel.h
@@ -0,0 +1,444 @@
+/**
+ * @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() 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;
+
+ // +-------------------------------------------------------------------+
+ // + Count
+ // +-------------------------------------------------------------------+
+ virtual void setFilterCount(S32 count) = 0;
+ virtual S32 getFilterCount() const = 0;
+ virtual void decrementFilterCount() = 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:
+ virtual ~LLFolderViewModelInterface() {}
+ virtual void requestSortAll() = 0;
+
+ virtual void sort(class LLFolderViewFolder*) = 0;
+ virtual void filter() = 0;
+
+ virtual bool contentsReady() = 0;
+ virtual void setFolderView(LLFolderView* folder_view) = 0;
+ virtual LLFolderViewFilter& getFilter() = 0;
+ virtual const LLFolderViewFilter& getFilter() const = 0;
+ virtual std::string getStatusText() = 0;
+
+ virtual bool startDrag(std::vector<LLFolderViewModelItem*>& 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 LLPointer<LLUIImage> getIcon() const = 0;
+ virtual LLPointer<LLUIImage> getIconOpen() const { return getIcon(); }
+ virtual LLPointer<LLUIImage> 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 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( void ) const = 0; // Can be destroyed
+ virtual BOOL removeItem() = 0;
+ virtual void removeBatch(std::vector<LLFolderViewModelItem*>& batch) = 0;
+
+ virtual BOOL isItemCopyable() const = 0;
+ virtual BOOL copyToClipboard() const = 0;
+ virtual BOOL cutToClipboard() const = 0;
+
+ 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 bool hasFilterStringMatch() = 0;
+ virtual std::string::size_type getFilterStringOffset() = 0;
+ virtual std::string::size_type getFilterStringSize() = 0;
+
+ virtual S32 getLastFilterGeneration() const = 0;
+
+ virtual bool hasChildren() const = 0;
+ virtual void addChild(LLFolderViewModelItem* child) = 0;
+ virtual void removeChild(LLFolderViewModelItem* child) = 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),
+ 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; }
+ void dirtyFilter()
+ {
+ mLastFilterGeneration = -1;
+ mLastFolderFilterGeneration = -1;
+
+ // bubble up dirty flag all the way to root
+ if (mParent)
+ {
+ mParent->dirtyFilter();
+ }
+ }
+ bool hasFilterStringMatch();
+ std::string::size_type getFilterStringOffset();
+ std::string::size_type getFilterStringSize();
+
+ typedef std::list<LLFolderViewModelItem*> child_list_t;
+
+ virtual void addChild(LLFolderViewModelItem* child)
+ {
+ // Avoid duplicates: bail out if that child is already present in the list
+ // Note: this happens when models are created before views
+ child_list_t::const_iterator iter;
+ for (iter = mChildren.begin(); iter != mChildren.end(); iter++)
+ {
+ if (child == *iter)
+ {
+ return;
+ }
+ }
+ mChildren.push_back(child);
+ child->setParent(this);
+ dirtyFilter();
+ requestSort();
+ }
+ virtual void removeChild(LLFolderViewModelItem* child)
+ {
+ mChildren.remove(child);
+ child->setParent(NULL);
+ dirtyFilter();
+ }
+
+ virtual void clearChildren()
+ {
+ // As this is cleaning the whole list of children wholesale, we do need to delete the pointed objects
+ // This is different and not equivalent to calling removeChild() on each child
+ std::for_each(mChildren.begin(), mChildren.end(), DeletePointer());
+ mChildren.clear();
+ 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;
+ }
+
+ 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
+ && (descendantsPassedFilter(filter_generation)
+ || passed_filter);
+ }
+
+ 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;
+ S32 mLastFolderFilterGeneration;
+ S32 mMostFilteredDescendantGeneration;
+
+ 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();
+ virtual void filter();
+
+ void setFolderView(LLFolderView* folder_view) { mFolderView = folder_view;}
+
+protected:
+ bool needsSort(class LLFolderViewModelItem* item);
+
+ S32 mTargetSortVersion;
+ LLFolderView* mFolderView;
+
+};
+
+template <typename SORT_TYPE, typename ITEM_TYPE, typename FOLDER_TYPE, typename FILTER_TYPE>
+class LLFolderViewModel : public LLFolderViewModelCommon
+{
+public:
+ LLFolderViewModel(){}
+ virtual ~LLFolderViewModel() {}
+
+ typedef SORT_TYPE SortType;
+ typedef ITEM_TYPE ItemType;
+ typedef FOLDER_TYPE FolderType;
+ typedef FILTER_TYPE FilterType;
+
+ virtual SortType& getSorter() { return mSorter; }
+ virtual const SortType& getSorter() const { return mSorter; }
+ virtual void setSorter(const SortType& sorter) { mSorter = sorter; requestSortAll(); }
+
+ virtual FilterType& getFilter() { return mFilter; }
+ virtual const FilterType& getFilter() const { return mFilter; }
+ virtual void setFilter(const FilterType& filter) { mFilter = 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; }
+
+
+ struct ViewModelCompare
+ {
+ ViewModelCompare(const SortType& sorter)
+ : mSorter(sorter)
+ {}
+
+ bool operator () (const LLFolderViewItem* a, const LLFolderViewItem* b) const
+ {
+ return mSorter(static_cast<const ItemType*>(a->getViewModelItem()), static_cast<const ItemType*>(b->getViewModelItem()));
+ }
+
+ bool operator () (const LLFolderViewFolder* a, const LLFolderViewFolder* b) const
+ {
+ return mSorter(static_cast<const ItemType*>(a->getViewModelItem()), static_cast<const ItemType*>(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:
+ SortType mSorter;
+ FilterType mFilter;
+};
+
+#endif // LLFOLDERVIEWMODEL_H
diff --git a/indra/llui/lllayoutstack.cpp b/indra/llui/lllayoutstack.cpp
index 4c730286da..e642883991 100644
--- a/indra/llui/lllayoutstack.cpp
+++ b/indra/llui/lllayoutstack.cpp
@@ -32,11 +32,10 @@
#include "lllocalcliprect.h"
#include "llpanel.h"
-#include "llresizebar.h"
#include "llcriticaldamp.h"
#include "boost/foreach.hpp"
-static const F32 MIN_FRACTIONAL_SIZE = 0.0f;
+static const F32 MIN_FRACTIONAL_SIZE = 0.00001f;
static const F32 MAX_FRACTIONAL_SIZE = 1.f;
static LLDefaultChildRegistry::Register<LLLayoutStack> register_layout_stack("layout_stack");
@@ -71,7 +70,7 @@ LLLayoutPanel::LLLayoutPanel(const Params& p)
mCollapseAmt(0.f),
mVisibleAmt(1.f), // default to fully visible
mResizeBar(NULL),
- mFractionalSize(MIN_FRACTIONAL_SIZE),
+ mFractionalSize(0.f),
mTargetDim(0),
mIgnoreReshape(false),
mOrientation(LLLayoutStack::HORIZONTAL)
@@ -521,7 +520,7 @@ void LLLayoutStack::updateFractionalSizes()
{
if (panelp->mAutoResize)
{
- total_resizable_dim += llmax(0, panelp->getLayoutDim() - panelp->getRelevantMinDim());
+ total_resizable_dim += llmax(MIN_FRACTIONAL_SIZE, (F32)(panelp->getLayoutDim() - panelp->getRelevantMinDim()));
}
}
@@ -672,12 +671,12 @@ void LLLayoutStack::updatePanelRect( LLLayoutPanel* resized_panel, const LLRect&
S32 new_dim = (mOrientation == HORIZONTAL)
? new_rect.getWidth()
: new_rect.getHeight();
- S32 delta_dim = new_dim - resized_panel->getVisibleDim();
- if (delta_dim == 0) return;
+ 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 original_auto_resize_headroom = 0.f;
+ F32 old_auto_resize_headroom = 0.f;
LLLayoutPanel* other_resize_panel = NULL;
LLLayoutPanel* following_panel = NULL;
@@ -686,7 +685,7 @@ void LLLayoutStack::updatePanelRect( LLLayoutPanel* resized_panel, const LLRect&
{
if (panelp->mAutoResize)
{
- original_auto_resize_headroom += (F32)(panelp->mTargetDim - panelp->getRelevantMinDim());
+ old_auto_resize_headroom += (F32)(panelp->mTargetDim - panelp->getRelevantMinDim());
if (panelp->getVisible() && !panelp->mCollapsed)
{
total_visible_fraction += panelp->mFractionalSize;
@@ -704,25 +703,24 @@ void LLLayoutStack::updatePanelRect( LLLayoutPanel* resized_panel, const LLRect&
}
}
-
if (resized_panel->mAutoResize)
{
if (!other_resize_panel || !other_resize_panel->mAutoResize)
{
- delta_auto_resize_headroom += delta_dim;
+ delta_auto_resize_headroom += delta_panel_dim;
}
}
else
{
if (!other_resize_panel || other_resize_panel->mAutoResize)
{
- delta_auto_resize_headroom -= delta_dim;
+ delta_auto_resize_headroom -= delta_panel_dim;
}
}
F32 fraction_given_up = 0.f;
F32 fraction_remaining = 1.f;
- F32 updated_auto_resize_headroom = original_auto_resize_headroom + delta_auto_resize_headroom;
+ F32 new_auto_resize_headroom = old_auto_resize_headroom + delta_auto_resize_headroom;
enum
{
@@ -734,7 +732,14 @@ void LLLayoutStack::updatePanelRect( LLLayoutPanel* resized_panel, const LLRect&
BOOST_FOREACH(LLLayoutPanel* panelp, mPanels)
{
- if (!panelp->getVisible() || panelp->mCollapsed) continue;
+ if (!panelp->getVisible() || panelp->mCollapsed)
+ {
+ if (panelp->mAutoResize)
+ {
+ fraction_remaining -= panelp->mFractionalSize;
+ }
+ continue;
+ }
if (panelp == resized_panel)
{
@@ -746,9 +751,9 @@ void LLLayoutStack::updatePanelRect( LLLayoutPanel* resized_panel, const LLRect&
case BEFORE_RESIZED_PANEL:
if (panelp->mAutoResize)
{ // freeze current size as fraction of overall auto_resize space
- F32 fractional_adjustment_factor = updated_auto_resize_headroom == 0.f
+ F32 fractional_adjustment_factor = new_auto_resize_headroom == 0.f
? 1.f
- : original_auto_resize_headroom / updated_auto_resize_headroom;
+ : old_auto_resize_headroom / new_auto_resize_headroom;
F32 new_fractional_size = llclamp(panelp->mFractionalSize * fractional_adjustment_factor,
MIN_FRACTIONAL_SIZE,
MAX_FRACTIONAL_SIZE);
@@ -765,9 +770,9 @@ void LLLayoutStack::updatePanelRect( LLLayoutPanel* resized_panel, const LLRect&
case RESIZED_PANEL:
if (panelp->mAutoResize)
{ // freeze new size as fraction
- F32 new_fractional_size = (updated_auto_resize_headroom == 0.f)
+ F32 new_fractional_size = (new_auto_resize_headroom == 0.f)
? MAX_FRACTIONAL_SIZE
- : llclamp(total_visible_fraction * (F32)(new_dim - panelp->getRelevantMinDim()) / updated_auto_resize_headroom, MIN_FRACTIONAL_SIZE, 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;
@@ -790,8 +795,13 @@ void LLLayoutStack::updatePanelRect( LLLayoutPanel* resized_panel, const LLRect&
}
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)
- / updated_auto_resize_headroom,
+ / new_auto_resize_headroom,
MIN_FRACTIONAL_SIZE,
MAX_FRACTIONAL_SIZE);
fraction_given_up -= new_fractional_size - panelp->mFractionalSize;
@@ -800,7 +810,7 @@ void LLLayoutStack::updatePanelRect( LLLayoutPanel* resized_panel, const LLRect&
}
else
{
- panelp->mTargetDim -= delta_dim;
+ panelp->mTargetDim -= delta_panel_dim;
}
which_panel = AFTER_RESIZED_PANEL;
break;
@@ -816,7 +826,7 @@ void LLLayoutStack::updatePanelRect( LLLayoutPanel* resized_panel, const LLRect&
}
}
updateLayout();
- normalizeFractionalSizes();
+ //normalizeFractionalSizes();
}
void LLLayoutStack::reshape(S32 width, S32 height, BOOL called_from_parent)
diff --git a/indra/llui/lllayoutstack.h b/indra/llui/lllayoutstack.h
index 648cd5fdce..02c664f1a0 100644
--- a/indra/llui/lllayoutstack.h
+++ b/indra/llui/lllayoutstack.h
@@ -29,6 +29,7 @@
#define LL_LLLAYOUTSTACK_H
#include "llpanel.h"
+#include "llresizebar.h"
class LLLayoutPanel;
@@ -178,6 +179,9 @@ public:
F32 getAutoResizeFactor() const;
F32 getVisibleAmount() const;
S32 getVisibleDim() const;
+ LLResizeBar* getResizeBar() { return mResizeBar; }
+
+ bool isCollapsed() const { return mCollapsed;}
void setOrientation(LLLayoutStack::ELayoutOrientation orientation);
void storeOriginalDim();
diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp
index 48d49af588..2e64be89fa 100644
--- a/indra/llui/lllineeditor.cpp
+++ b/indra/llui/lllineeditor.cpp
@@ -157,8 +157,7 @@ LLLineEditor::LLLineEditor(const LLLineEditor::Params& p)
mHighlightColor(p.highlight_color()),
mPreeditBgColor(p.preedit_bg_color()),
mGLFont(p.font),
- mContextMenuHandle(),
- mAutoreplaceCallback()
+ mContextMenuHandle()
{
llassert( mMaxLengthBytes > 0 );
@@ -971,12 +970,6 @@ void LLLineEditor::addChar(const llwchar uni_char)
LLUI::reportBadKeystroke();
}
- if (!mReadOnly && mAutoreplaceCallback != NULL)
- {
- // call callback
- mAutoreplaceCallback(mText, mCursorPos);
- }
-
getWindow()->hideCursorUntilMouseMove();
}
diff --git a/indra/llui/lllineeditor.h b/indra/llui/lllineeditor.h
index 71dd53f608..40f931ecc1 100644
--- a/indra/llui/lllineeditor.h
+++ b/indra/llui/lllineeditor.h
@@ -189,9 +189,6 @@ public:
virtual BOOL setTextArg( const std::string& key, const LLStringExplicit& text );
virtual BOOL setLabelArg( const std::string& key, const LLStringExplicit& text );
- typedef boost::function<void(LLUIString&, S32&)> autoreplace_callback_t;
- autoreplace_callback_t mAutoreplaceCallback;
- void setAutoreplaceCallback(autoreplace_callback_t cb) { mAutoreplaceCallback = cb; }
void setLabel(const LLStringExplicit &new_label) { mLabel = new_label; }
const std::string& getLabel() { return mLabel.getString(); }
diff --git a/indra/llui/llloadingindicator.cpp b/indra/llui/llloadingindicator.cpp
index 6ac38f5ad4..1ede5b706f 100644
--- a/indra/llui/llloadingindicator.cpp
+++ b/indra/llui/llloadingindicator.cpp
@@ -52,7 +52,7 @@ LLLoadingIndicator::LLLoadingIndicator(const Params& p)
void LLLoadingIndicator::initFromParams(const Params& p)
{
- BOOST_FOREACH(LLUIImage* image, p.images.image)
+ BOOST_FOREACH(LLUIImage* image, p.images().image)
{
mImages.push_back(image);
}
diff --git a/indra/llui/llloadingindicator.h b/indra/llui/llloadingindicator.h
index c1f979c111..ffcb329f42 100644
--- a/indra/llui/llloadingindicator.h
+++ b/indra/llui/llloadingindicator.h
@@ -51,7 +51,7 @@ class LLLoadingIndicator
LOG_CLASS(LLLoadingIndicator);
public:
- struct Images : public LLInitParam::BatchBlock<Images>
+ struct Images : public LLInitParam::Block<Images>
{
Multiple<LLUIImage*> image;
@@ -62,8 +62,8 @@ public:
struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
{
- Optional<F32> images_per_sec;
- Optional<Images> images;
+ Optional<F32> images_per_sec;
+ Optional<Atomic<Images> > images;
Params()
: images_per_sec("images_per_sec", 1.0f),
diff --git a/indra/llui/llmenubutton.cpp b/indra/llui/llmenubutton.cpp
index 50d59f79f4..746ade4648 100644
--- a/indra/llui/llmenubutton.cpp
+++ b/indra/llui/llmenubutton.cpp
@@ -44,33 +44,27 @@ void LLMenuButton::MenuPositions::declareValues()
LLMenuButton::Params::Params()
: menu_filename("menu_filename"),
- position("position", MP_BOTTOM_LEFT)
+ position("menu_position", MP_BOTTOM_LEFT)
{
+ addSynonym(position, "position");
}
LLMenuButton::LLMenuButton(const LLMenuButton::Params& p)
: LLButton(p),
mIsMenuShown(false),
- mMenuPosition(p.position)
+ mMenuPosition(p.position),
+ mOwnMenu(false)
{
std::string menu_filename = p.menu_filename;
- if (!menu_filename.empty())
- {
- LLToggleableMenu* menu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>(menu_filename, LLMenuGL::sMenuContainer, LLMenuHolderGL::child_registry_t::instance());
- if (!menu)
- {
- llwarns << "Error loading menu_button menu" << llendl;
- return;
- }
-
- menu->setVisibilityChangeCallback(boost::bind(&LLMenuButton::onMenuVisibilityChange, this, _2));
-
- mMenuHandle = menu->getHandle();
+ setMenu(menu_filename, mMenuPosition);
+ updateMenuOrigin();
+}
- updateMenuOrigin();
- }
+LLMenuButton::~LLMenuButton()
+{
+ cleanup();
}
boost::signals2::connection LLMenuButton::setMouseDownCallback( const mouse_signal_t::slot_type& cb )
@@ -80,9 +74,7 @@ boost::signals2::connection LLMenuButton::setMouseDownCallback( const mouse_sign
void LLMenuButton::hideMenu()
{
- if(mMenuHandle.isDead()) return;
-
- LLToggleableMenu* menu = dynamic_cast<LLToggleableMenu*>(mMenuHandle.get());
+ LLToggleableMenu* menu = getMenu();
if (menu)
{
menu->setVisible(FALSE);
@@ -94,19 +86,39 @@ LLToggleableMenu* LLMenuButton::getMenu()
return dynamic_cast<LLToggleableMenu*>(mMenuHandle.get());
}
-void LLMenuButton::setMenu(LLToggleableMenu* menu, EMenuPosition position /*MP_TOP_LEFT*/)
+void LLMenuButton::setMenu(const std::string& menu_filename, EMenuPosition position /*MP_TOP_LEFT*/)
+{
+ if (menu_filename.empty())
+ {
+ return;
+ }
+
+ LLToggleableMenu* menu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>(menu_filename, LLMenuGL::sMenuContainer, LLMenuHolderGL::child_registry_t::instance());
+ if (!menu)
+ {
+ llwarns << "Error loading menu_button menu" << llendl;
+ 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 (mMenuHandle.isDead()) return FALSE;
+ if (!getMenu()) return FALSE;
if( KEY_RETURN == key && mask == MASK_NONE && !gKeyboard->getKeyRepeated(key))
{
@@ -118,7 +130,7 @@ BOOL LLMenuButton::handleKeyHere(KEY key, MASK mask )
return TRUE;
}
- LLToggleableMenu* menu = dynamic_cast<LLToggleableMenu*>(mMenuHandle.get());
+ LLToggleableMenu* menu = getMenu();
if (menu && menu->getVisible() && key == KEY_ESCAPE && mask == MASK_NONE)
{
menu->setVisible(FALSE);
@@ -139,9 +151,12 @@ BOOL LLMenuButton::handleMouseDown(S32 x, S32 y, MASK mask)
void LLMenuButton::toggleMenu()
{
- if(mMenuHandle.isDead()) return;
+ if (mValidateSignal && !(*mValidateSignal)(this, LLSD()))
+ {
+ return;
+ }
- LLToggleableMenu* menu = dynamic_cast<LLToggleableMenu*>(mMenuHandle.get());
+ LLToggleableMenu* menu = getMenu();
if (!menu) return;
// Store the button rectangle to toggle menu visibility if a mouse event
@@ -170,7 +185,8 @@ void LLMenuButton::toggleMenu()
void LLMenuButton::updateMenuOrigin()
{
- if (mMenuHandle.isDead()) return;
+ LLToggleableMenu* menu = getMenu();
+ if (!menu) return;
LLRect rect = getRect();
@@ -179,12 +195,12 @@ void LLMenuButton::updateMenuOrigin()
case MP_TOP_LEFT:
{
mX = rect.mLeft;
- mY = rect.mTop + mMenuHandle.get()->getRect().getHeight();
+ mY = rect.mTop + menu->getRect().getHeight();
break;
}
case MP_TOP_RIGHT:
{
- const LLRect& menu_rect = mMenuHandle.get()->getRect();
+ const LLRect& menu_rect = menu->getRect();
mX = rect.mRight - menu_rect.getWidth();
mY = rect.mTop + menu_rect.getHeight();
break;
@@ -211,3 +227,11 @@ void LLMenuButton::onMenuVisibilityChange(const LLSD& param)
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 e2396e7fb2..67ec1983b3 100644
--- a/indra/llui/llmenubutton.h
+++ b/indra/llui/llmenubutton.h
@@ -34,6 +34,8 @@ class LLToggleableMenu;
class LLMenuButton
: public LLButton
{
+ LOG_CLASS(LLMenuButton);
+
public:
typedef enum e_menu_position
{
@@ -53,7 +55,7 @@ public:
{
// filename for it's toggleable menu
Optional<std::string> menu_filename;
- Optional<EMenuPosition> position;
+ Optional<EMenuPosition, MenuPositions> position;
Params();
};
@@ -68,13 +70,15 @@ public:
void hideMenu();
LLToggleableMenu* getMenu();
- void setMenu(LLToggleableMenu* menu, EMenuPosition position = MP_TOP_LEFT);
+ 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();
@@ -82,11 +86,14 @@ protected:
void onMenuVisibilityChange(const LLSD& param);
private:
+ void cleanup();
+
LLHandle<LLView> mMenuHandle;
bool mIsMenuShown;
EMenuPosition mMenuPosition;
S32 mX;
S32 mY;
+ bool mOwnMenu; // true if we manage the menu lifetime
};
diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp
index cd6cc6a75e..f7bf39c897 100644
--- a/indra/llui/llmenugl.cpp
+++ b/indra/llui/llmenugl.cpp
@@ -593,12 +593,12 @@ BOOL LLMenuItemSeparatorGL::handleMouseDown(S32 x, S32 y, MASK mask)
{
// the menu items are in the child list in bottom up order
LLView* prev_menu_item = parent_menu->findNextSibling(this);
- return prev_menu_item ? prev_menu_item->handleMouseDown(x, prev_menu_item->getRect().getHeight(), mask) : FALSE;
+ 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->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;
}
}
@@ -608,12 +608,12 @@ BOOL LLMenuItemSeparatorGL::handleMouseUp(S32 x, S32 y, MASK mask)
if (y > getRect().getHeight() / 2)
{
LLView* prev_menu_item = parent_menu->findNextSibling(this);
- return prev_menu_item ? 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->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;
}
}
@@ -1751,35 +1751,50 @@ void LLMenuGL::setCanTearOff(BOOL tear_off)
bool LLMenuGL::addChild(LLView* view, S32 tab_group)
{
- if (LLMenuGL* menup = dynamic_cast<LLMenuGL*>(view))
+ LLMenuGL* menup = dynamic_cast<LLMenuGL*>(view);
+ if (menup)
{
- appendMenu(menup);
- return true;
+ return appendMenu(menup);
}
- else if (LLMenuItemGL* itemp = dynamic_cast<LLMenuItemGL*>(view))
+
+ LLMenuItemGL* itemp = dynamic_cast<LLMenuItemGL*>(view);
+ if (itemp)
{
- append(itemp);
- return true;
+ return append(itemp);
}
+
return false;
}
// Used in LLContextMenu and in LLTogleableMenu
-// to add an item of context menu branch
+
+// Add an item to the context menu branch
bool LLMenuGL::addContextChild(LLView* view, S32 tab_group)
{
LLContextMenu* context = dynamic_cast<LLContextMenu*>(view);
if (context)
+ {
return appendContextSubMenu(context);
+ }
LLMenuItemSeparatorGL* separator = dynamic_cast<LLMenuItemSeparatorGL*>(view);
if (separator)
+ {
return append(separator);
+ }
LLMenuItemGL* item = dynamic_cast<LLMenuItemGL*>(view);
if (item)
+ {
return append(item);
-
+ }
+
+ LLMenuGL* menup = dynamic_cast<LLMenuGL*>(view);
+ if (menup)
+ {
+ return appendMenu(menup);
+ }
+
return false;
}
@@ -2446,6 +2461,56 @@ void LLMenuGL::empty( void )
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<LLMenuItemGL *>(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)
{
@@ -2487,7 +2552,8 @@ BOOL LLMenuGL::append( LLMenuItemGL* item )
// add a separator to this menu
BOOL LLMenuGL::addSeparator()
{
- LLMenuItemGL* separator = new LLMenuItemSeparatorGL();
+ LLMenuItemSeparatorGL::Params p;
+ LLMenuItemGL* separator = LLUICtrlFactory::create<LLMenuItemSeparatorGL>(p);
return addChild(separator);
}
@@ -3080,7 +3146,17 @@ void LLMenuGL::showPopup(LLView* spawning_view, LLMenuGL* menu, S32 x, S32 y)
const S32 CURSOR_HEIGHT = 22; // Approximate "normal" cursor size
const S32 CURSOR_WIDTH = 12;
- if(menu->getChildList()->empty())
+ //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(menu->getChildList()->empty() || !item_enabled)
{
return;
}
@@ -4039,11 +4115,6 @@ BOOL LLContextMenu::handleRightMouseUp( S32 x, S32 y, MASK mask )
return result;
}
-void LLContextMenu::draw()
-{
- LLMenuGL::draw();
-}
-
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 00899020bc..51df5df1f8 100644
--- a/indra/llui/llmenugl.h
+++ b/indra/llui/llmenugl.h
@@ -478,6 +478,12 @@ public:
// 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
@@ -675,8 +681,6 @@ public:
// can't set visibility directly, must call show or hide
virtual void setVisible (BOOL visible);
- virtual void draw ();
-
virtual void show (S32 x, S32 y, LLView* spawning_view = NULL);
virtual void hide ();
@@ -698,7 +702,6 @@ protected:
LLHandle<LLView> mSpawningViewHandle;
};
-
//-----------------------------------------------------------------------------
// class LLContextMenuBranch
// A branch to another context menu
diff --git a/indra/llui/llmultifloater.cpp b/indra/llui/llmultifloater.cpp
index aa5f577897..179b251cdb 100644
--- a/indra/llui/llmultifloater.cpp
+++ b/indra/llui/llmultifloater.cpp
@@ -41,8 +41,8 @@ LLMultiFloater::LLMultiFloater(const LLSD& key, const LLFloater::Params& params)
mTabContainer(NULL),
mTabPos(LLTabContainer::TOP),
mAutoResize(TRUE),
- mOrigMinWidth(0),
- mOrigMinHeight(0)
+ mOrigMinWidth(params.min_width),
+ mOrigMinHeight(params.min_height)
{
}
@@ -173,7 +173,7 @@ void LLMultiFloater::addFloater(LLFloater* floaterp, BOOL select_added_floater,
else if (floaterp->getHost())
{
// floaterp is hosted by somebody else and
- // this is adding it, so remove it from it's old host
+ // this is adding it, so remove it from its old host
floaterp->getHost()->removeFloater(floaterp);
}
else if (floaterp->getParent() == gFloaterView)
@@ -188,11 +188,13 @@ void LLMultiFloater::addFloater(LLFloater* floaterp, BOOL select_added_floater,
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);
@@ -291,6 +293,7 @@ void LLMultiFloater::removeFloater(LLFloater* floaterp)
{
LLFloaterData& floater_data = found_data_it->second;
floaterp->setCanMinimize(floater_data.mCanMinimize);
+ floaterp->mSaveRect = floater_data.mSaveRect;
if (!floater_data.mCanResize)
{
// restore original size
@@ -468,23 +471,12 @@ BOOL LLMultiFloater::postBuild()
void LLMultiFloater::updateResizeLimits()
{
- static LLUICachedControl<S32> 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;
// initialize minimum size constraint to the original xml values.
S32 new_min_width = mOrigMinWidth;
S32 new_min_height = mOrigMinHeight;
- // possibly increase minimum size constraint due to children's minimums.
- for (S32 tab_idx = 0; tab_idx < mTabContainer->getTabCount(); ++tab_idx)
- {
- LLFloater* floaterp = (LLFloater*)mTabContainer->getPanelByIndex(tab_idx);
- if (floaterp)
- {
- new_min_width = llmax(new_min_width, floaterp->getMinWidth() + LLPANEL_BORDER_WIDTH * 2);
- new_min_height = llmax(new_min_height, floaterp->getMinHeight() + floater_header_size + tabcntr_header_height);
- }
- }
+
+ computeResizeLimits(new_min_width, new_min_height);
+
setResizeLimits(new_min_width, new_min_height);
S32 cur_height = getRect().getHeight();
@@ -510,3 +502,22 @@ void LLMultiFloater::updateResizeLimits()
gFloaterView->adjustToFitScreen(this, TRUE);
}
}
+
+void LLMultiFloater::computeResizeLimits(S32& new_min_width, S32& new_min_height)
+{
+ static LLUICachedControl<S32> 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 9fa917eca1..d992212650 100644
--- a/indra/llui/llmultifloater.h
+++ b/indra/llui/llmultifloater.h
@@ -45,8 +45,8 @@ public:
virtual BOOL postBuild();
/*virtual*/ void onOpen(const LLSD& key);
- /*virtual*/ void draw();
- /*virtual*/ void setVisible(BOOL visible);
+ virtual void draw();
+ virtual void setVisible(BOOL visible);
/*virtual*/ BOOL handleKeyHere(KEY key, MASK mask);
/*virtual*/ bool addChild(LLView* view, S32 tab_group = 0);
@@ -79,10 +79,11 @@ public:
protected:
struct LLFloaterData
{
- S32 mWidth;
- S32 mHeight;
- BOOL mCanMinimize;
- BOOL mCanResize;
+ S32 mWidth;
+ S32 mHeight;
+ BOOL mCanMinimize;
+ BOOL mCanResize;
+ BOOL mSaveRect;
};
LLTabContainer* mTabContainer;
@@ -93,6 +94,9 @@ protected:
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/llnotifications.cpp b/indra/llui/llnotifications.cpp
index 2bf88532c6..1789f003b9 100644
--- a/indra/llui/llnotifications.cpp
+++ b/indra/llui/llnotifications.cpp
@@ -39,7 +39,6 @@
#include "lldir.h"
#include "llsdserialize.h"
#include "lltrans.h"
-#include "llnotificationslistener.h"
#include "llstring.h"
#include "llsdparam.h"
#include "llsdutil.h"
@@ -60,7 +59,8 @@ void NotificationPriorityValues::declareValues()
}
LLNotificationForm::FormElementBase::FormElementBase()
-: name("name")
+: name("name"),
+ enabled("enabled", true)
{}
LLNotificationForm::FormIgnore::FormIgnore()
@@ -104,39 +104,7 @@ LLNotificationForm::Params::Params()
form_elements("")
{}
-// Local channel for persistent notifications
-// Stores only persistent notifications.
-// Class users can use connectChanged() to process persistent notifications
-// (see LLNotificationStorage for example).
-class LLPersistentNotificationChannel : public LLNotificationChannel
-{
- LOG_CLASS(LLPersistentNotificationChannel);
-public:
- LLPersistentNotificationChannel() :
- LLNotificationChannel("Persistent", "Visible", &notificationFilter, LLNotificationComparators::orderByUUID())
- {
- }
-
-private:
- // 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 onDelete(LLNotificationPtr pNotification)
- {
- // we want to keep deleted notifications in our log, otherwise some
- // notifications will be lost on exit.
- mItems.insert(pNotification);
- }
-};
bool filterIgnoredNotifications(LLNotificationPtr notification)
{
@@ -210,6 +178,14 @@ LLNotificationForm::LLNotificationForm()
{
}
+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),
@@ -246,14 +222,6 @@ LLNotificationForm::LLNotificationForm(const std::string& name, const LLNotifica
LLParamSDParser parser;
parser.writeSD(mFormData, p.form_elements);
- if (!mFormData.isArray())
- {
- // change existing contents to a one element array
- LLSD new_llsd_array = LLSD::emptyArray();
- new_llsd_array.append(mFormData);
- mFormData = new_llsd_array;
- }
-
for (LLSD::array_iterator it = mFormData.beginArray(), end_it = mFormData.endArray();
it != end_it;
++it)
@@ -300,7 +268,7 @@ LLSD LLNotificationForm::getElement(const std::string& element_name)
}
-bool LLNotificationForm::hasElement(const std::string& element_name)
+bool LLNotificationForm::hasElement(const std::string& element_name) const
{
for (LLSD::array_const_iterator it = mFormData.beginArray();
it != mFormData.endArray();
@@ -311,7 +279,48 @@ bool LLNotificationForm::hasElement(const std::string& element_name)
return false;
}
-void LLNotificationForm::addElement(const std::string& type, const std::string& name, const LLSD& value)
+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;
@@ -319,6 +328,7 @@ void LLNotificationForm::addElement(const std::string& type, const std::string&
element["text"] = name;
element["value"] = value;
element["index"] = mFormData.size();
+ element["enabled"] = enabled;
mFormData.append(element);
}
@@ -408,14 +418,19 @@ LLNotificationTemplate::LLNotificationTemplate(const LLNotificationTemplate::Par
mURLOption(p.url.option),
mURLTarget(p.url.target),
mUnique(p.unique.isProvided()),
+ mCombineBehavior(p.unique.combine),
mPriority(p.priority),
mPersist(p.persist),
- mDefaultFunctor(p.functor.isProvided() ? p.functor() : p.name())
+ mDefaultFunctor(p.functor.isProvided() ? p.functor() : p.name()),
+ mLogToChat(p.log_to_chat),
+ mLogToIM(p.log_to_im),
+ mShowToast(p.show_toast),
+ mSoundName("")
{
if (p.sound.isProvided()
&& LLUI::sSettingGroups["config"]->controlExists(p.sound))
{
- mSoundEffect = LLUUID(LLUI::sSettingGroups["config"]->getString(p.sound));
+ mSoundName = p.sound;
}
BOOST_FOREACH(const LLNotificationTemplate::UniquenessContext& context, p.unique.contexts)
@@ -460,18 +475,20 @@ LLNotificationVisibilityRule::LLNotificationVisibilityRule(const LLNotificationV
}
}
-LLNotification::LLNotification(const LLNotification::Params& p) :
+LLNotification::LLNotification(const LLSDParamAdapter<Params>& p) :
mTimestamp(p.time_stamp),
mSubstitutions(p.substitutions),
mPayload(p.payload),
- mExpiresAt(0),
+ mExpiresAt(p.expiry),
mTemporaryResponder(false),
mRespondedTo(false),
mPriority(p.priority),
mCancelled(false),
mIgnored(false),
mResponderObj(NULL),
- mIsReusable(false)
+ mId(p.id.isProvided() ? p.id : LLUUID::generateNewID()),
+ mOfferFromAgent(p.offer_from_agent),
+ mIsDND(p.is_dnd)
{
if (p.functor.name.isChosen())
{
@@ -494,52 +511,52 @@ LLNotification::LLNotification(const LLNotification::Params& p) :
mResponderObj = p.responder;
}
- mId.generate();
init(p.name, p.form_elements);
}
-LLNotification::LLNotification(const LLSD& sd) :
- mTemporaryResponder(false),
- mRespondedTo(false),
- mCancelled(false),
- mIgnored(false),
- mResponderObj(NULL),
- mIsReusable(false)
-{
- mId.generate();
- mSubstitutions = sd["substitutions"];
- mPayload = sd["payload"];
- mTimestamp = sd["time"];
- mExpiresAt = sd["expiry"];
- mPriority = (ENotificationPriority)sd["priority"].asInteger();
- mResponseFunctorName = sd["responseFunctor"].asString();
- std::string templatename = sd["name"].asString();
- init(templatename, LLSD());
- // replace form with serialized version
- mForm = LLNotificationFormPtr(new LLNotificationForm(sd["form"]));
-}
-
-
-LLSD LLNotification::asLLSD()
+LLSD LLNotification::asLLSD(bool excludeTemplateElements)
{
- LLSD output;
- output["id"] = mId;
- output["name"] = mTemplatep->mName;
- output["form"] = getForm()->asLLSD();
- output["substitutions"] = mSubstitutions;
- output["payload"] = mPayload;
- output["time"] = mTimestamp;
- output["expiry"] = mExpiresAt;
- output["priority"] = (S32)mPriority;
- output["responseFunctor"] = mResponseFunctorName;
- output["reusable"] = mIsReusable;
+ LLParamSDParser parser;
- if(mResponder)
- {
- output["responder"] = mResponder->asLLSD();
+ 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;
}
@@ -569,7 +586,6 @@ void LLNotification::updateFrom(LLNotificationPtr other)
mRespondedTo = other->mRespondedTo;
mResponse = other->mResponse;
mTemporaryResponder = other->mTemporaryResponder;
- mIsReusable = other->isReusable();
update();
}
@@ -668,7 +684,7 @@ void LLNotification::respond(const LLSD& response)
return;
}
- if (mTemporaryResponder && !isReusable())
+ if (mTemporaryResponder)
{
LLNotificationFunctorRegistry::instance().unregisterFunctor(mResponseFunctorName);
mResponseFunctorName = "";
@@ -829,7 +845,7 @@ void LLNotification::init(const std::string& template_name, const LLSD& form_ele
//mSubstitutions["_ARGS"] = get_all_arguments_as_text(mSubstitutions);
mForm = LLNotificationFormPtr(new LLNotificationForm(*mTemplatep->mForm));
- mForm->append(form_elements);
+ mForm->append(form_elements);
// apply substitution to form labels
mForm->formatElements(mSubstitutions);
@@ -897,6 +913,49 @@ std::string LLNotification::getURL() const
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::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
// ---
@@ -957,7 +1016,7 @@ bool LLNotificationChannelBase::updateItem(const LLSD& payload, LLNotificationPt
std::string cmd = payload["sigtype"];
LLNotificationSet::iterator foundItem = mItems.find(pNotification);
bool wasFound = (foundItem != mItems.end());
- bool passesFilter = mFilter(pNotification);
+ 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.
@@ -966,10 +1025,12 @@ bool LLNotificationChannelBase::updateItem(const LLSD& payload, LLNotificationPt
bool abortProcessing = false;
if (passesFilter)
{
+ onFilterPass(pNotification);
abortProcessing = mPassedFilter(payload);
}
else
{
+ onFilterFail(pNotification);
abortProcessing = mFailedFilter(payload);
}
@@ -987,8 +1048,8 @@ bool LLNotificationChannelBase::updateItem(const LLSD& payload, LLNotificationPt
{
// not in our list, add it and say so
mItems.insert(pNotification);
- abortProcessing = mChanged(payload);
onLoad(pNotification);
+ abortProcessing = mChanged(payload);
}
}
else if (cmd == "change")
@@ -1003,18 +1064,18 @@ bool LLNotificationChannelBase::updateItem(const LLSD& payload, LLNotificationPt
{
// it already existed, so this is a change
// since it changed in place, all we have to do is resend the signal
- abortProcessing = mChanged(payload);
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);
- onChange(pNotification);
}
}
else
@@ -1023,11 +1084,11 @@ bool LLNotificationChannelBase::updateItem(const LLSD& payload, LLNotificationPt
{
// 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);
- onChange(pNotification);
}
// didn't pass, not on our list, do nothing
}
@@ -1041,8 +1102,8 @@ bool LLNotificationChannelBase::updateItem(const LLSD& payload, LLNotificationPt
{
// not in our list, add it and say so
mItems.insert(pNotification);
- abortProcessing = mChanged(payload);
onAdd(pNotification);
+ abortProcessing = mChanged(payload);
}
}
else if (cmd == "delete")
@@ -1050,65 +1111,35 @@ bool LLNotificationChannelBase::updateItem(const LLSD& payload, LLNotificationPt
// if we have it in our list, pass on the delete, then delete it, else do nothing
if (wasFound)
{
+ onDelete(pNotification);
abortProcessing = mChanged(payload);
- // do not delete the notification to make LLChatHistory::appendMessage add notification panel to IM window
- if( ! pNotification->isReusable() )
- {
- mItems.erase(pNotification);
- onDelete(pNotification);
- }
+ mItems.erase(pNotification);
}
}
return abortProcessing;
}
-/* static */
-LLNotificationChannelPtr LLNotificationChannel::buildChannel(const std::string& name,
- const std::string& parent,
- LLNotificationFilter filter,
- LLNotificationComparator comparator)
+LLNotificationChannel::LLNotificationChannel(const Params& p)
+: LLNotificationChannelBase(p.filter()),
+ LLInstanceTracker<LLNotificationChannel, std::string>(p.name.isProvided() ? p.name : LLUUID::generateNewID().asString()),
+ mName(p.name.isProvided() ? p.name : LLUUID::generateNewID().asString())
+{
+ BOOST_FOREACH(const std::string& source, p.sources)
{
- // note: this is not a leak; notifications are self-registering.
- // This factory helps to prevent excess deletions by making sure all smart
- // pointers to notification channels come from the same source
- new LLNotificationChannel(name, parent, filter, comparator);
- return LLNotifications::instance().getChannel(name);
+ connectToChannel(source);
+ }
}
LLNotificationChannel::LLNotificationChannel(const std::string& name,
const std::string& parent,
- LLNotificationFilter filter,
- LLNotificationComparator comparator) :
-LLNotificationChannelBase(filter, comparator),
-mName(name),
-mParent(parent)
-{
- // store myself in the channel map
- LLNotifications::instance().addChannel(LLNotificationChannelPtr(this));
+ LLNotificationFilter filter)
+: LLNotificationChannelBase(filter),
+ LLInstanceTracker<LLNotificationChannel, std::string>(name),
+ mName(name)
+{
// bind to notification broadcast
- if (parent.empty())
- {
- LLNotifications::instance().connectChanged(
- boost::bind(&LLNotificationChannelBase::updateItem, this, _1));
- }
- else
- {
- LLNotificationChannelPtr p = LLNotifications::instance().getChannel(parent);
- p->connectChanged(boost::bind(&LLNotificationChannelBase::updateItem, this, _1));
- }
-}
-
-
-void LLNotificationChannel::setComparator(LLNotificationComparator comparator)
-{
- mComparator = comparator;
- LLNotificationSet s2(mComparator);
- s2.insert(mItems.begin(), mItems.end());
- mItems.swap(s2);
-
- // notify clients that we've been resorted
- mChanged(LLSD().with("sigtype", "sort"));
+ connectToChannel(parent);
}
bool LLNotificationChannel::isEmpty() const
@@ -1131,6 +1162,11 @@ LLNotificationChannel::Iterator LLNotificationChannel::end()
return mItems.end();
}
+size_t LLNotificationChannel::size()
+{
+ return mItems.size();
+}
+
std::string LLNotificationChannel::summarize()
{
std::string s("Channel '");
@@ -1144,22 +1180,33 @@ std::string LLNotificationChannel::summarize()
return s;
}
+void LLNotificationChannel::connectToChannel( const std::string& channel_name )
+{
+ if (channel_name.empty())
+ {
+ LLNotifications::instance().connectChanged(
+ boost::bind(&LLNotificationChannelBase::updateItem, this, _1));
+ }
+ else
+ {
+ LLNotificationChannelPtr p = LLNotifications::instance().getChannel(channel_name);
+ p->connectChanged(boost::bind(&LLNotificationChannelBase::updateItem, this, _1));
+ }
+}
// ---
// END OF LLNotificationChannel implementation
// =========================================================
-// =========================================================
+// ============================================== ===========
// LLNotifications implementation
// ---
-LLNotifications::LLNotifications() : LLNotificationChannelBase(LLNotificationFilters::includeEverything,
- LLNotificationComparators::orderByUUID()),
- mIgnoreAllNotifications(false)
+LLNotifications::LLNotifications()
+: LLNotificationChannelBase(LLNotificationFilters::includeEverything),
+ mIgnoreAllNotifications(false)
{
LLUICtrl::CommitCallbackRegistry::currentRegistrar().add("Notification.Show", boost::bind(&LLNotifications::addFromCallback, this, _2));
-
- mListener.reset(new LLNotificationsListener(*this));
}
@@ -1196,7 +1243,15 @@ bool LLNotifications::uniqueFilter(LLNotificationPtr pNotif)
if (pNotif != existing_notification
&& pNotif->isEquivalentTo(existing_notification))
{
- return false;
+ if (pNotif->getCombineBehavior() == LLNotification::CANCEL_OLD)
+ {
+ cancel(existing_notification);
+ return true;
+ }
+ else
+ {
+ return false;
+ }
}
}
@@ -1236,43 +1291,43 @@ bool LLNotifications::failedUniquenessTest(const LLSD& payload)
return false;
}
- // 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)
+ switch(pNotif->getCombineBehavior())
{
- LLNotificationPtr existing_notification = existing_it->second;
- if (pNotif != existing_notification
- && pNotif->isEquivalentTo(existing_notification))
+ 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)
{
- // 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);
+ 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::KEEP_OLD:
+ break;
+ case LLNotification::CANCEL_OLD:
+ // already handled by filter logic
+ break;
+ default:
+ break;
}
return false;
}
-
-void LLNotifications::addChannel(LLNotificationChannelPtr pChan)
-{
- mChannels[pChan->getName()] = pChan;
-}
-
LLNotificationChannelPtr LLNotifications::getChannel(const std::string& channelName)
{
- ChannelMap::iterator p = mChannels.find(channelName);
- if(p == mChannels.end())
- {
- llerrs << "Did not find channel named " << channelName << llendl;
- return LLNotificationChannelPtr();
- }
- return p->second;
+ return LLNotificationChannelPtr(LLNotificationChannel::getInstance(channelName));
}
@@ -1288,24 +1343,21 @@ void LLNotifications::createDefaultChannels()
{
// now construct the various channels AFTER loading the notifications,
// because the history channel is going to rewrite the stored notifications file
- LLNotificationChannel::buildChannel("Enabled", "",
- !boost::bind(&LLNotifications::getIgnoreAllNotifications, this));
- LLNotificationChannel::buildChannel("Expiration", "Enabled",
- boost::bind(&LLNotifications::expirationFilter, this, _1));
- LLNotificationChannel::buildChannel("Unexpired", "Enabled",
- !boost::bind(&LLNotifications::expirationFilter, this, _1)); // use negated bind
- LLNotificationChannel::buildChannel("Unique", "Unexpired",
- boost::bind(&LLNotifications::uniqueFilter, this, _1));
- LLNotificationChannel::buildChannel("Ignore", "Unique",
- filterIgnoredNotifications);
- LLNotificationChannel::buildChannel("VisibilityRules", "Ignore",
- boost::bind(&LLNotifications::isVisibleByRules, this, _1));
- LLNotificationChannel::buildChannel("Visible", "VisibilityRules",
- &LLNotificationFilters::includeEverything);
-
- // create special persistent notification channel
- // this isn't a leak, don't worry about the empty "new"
- new LLPersistentNotificationChannel();
+ 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
LLNotifications::instance().getChannel("Enabled")->
@@ -1537,34 +1589,32 @@ void LLNotifications::addFromCallback(const LLSD& name)
add(name.asString(), LLSD(), LLSD());
}
-LLNotificationPtr LLNotifications::add(const std::string& name,
- const LLSD& substitutions,
- const LLSD& payload)
+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)
+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));
+ 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)
+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));
+ 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
@@ -1595,12 +1645,11 @@ void LLNotifications::cancel(LLNotificationPtr pNotif)
if (pNotif == NULL || pNotif->isCancelled()) return;
LLNotificationSet::iterator it=mItems.find(pNotif);
- if (it == mItems.end())
+ if (it != mItems.end())
{
- llerrs << "Attempted to delete nonexistent notification " << pNotif->getName() << llendl;
+ pNotif->cancel();
+ updateItem(LLSD().with("sigtype", "delete").with("id", pNotif->id()), pNotif);
}
- pNotif->cancel();
- updateItem(LLSD().with("sigtype", "delete").with("id", pNotif->id()), pNotif);
}
void LLNotifications::cancelByName(const std::string& name)
@@ -1639,7 +1688,7 @@ void LLNotifications::update(const LLNotificationPtr pNotif)
LLNotificationPtr LLNotifications::find(LLUUID uuid)
{
- LLNotificationPtr target = LLNotificationPtr(new LLNotification(uuid));
+ LLNotificationPtr target = LLNotificationPtr(new LLNotification(LLNotification::Params().id(uuid)));
LLNotificationSet::iterator it=mItems.find(target);
if (it == mItems.end())
{
@@ -1778,22 +1827,18 @@ std::ostream& operator<<(std::ostream& s, const LLNotification& notification)
return s;
}
-//static
-void LLPostponedNotification::lookupName(LLPostponedNotification* thiz,
- const LLUUID& id,
+void LLPostponedNotification::lookupName(const LLUUID& id,
bool is_group)
{
if (is_group)
{
gCacheName->getGroup(id,
boost::bind(&LLPostponedNotification::onGroupNameCache,
- thiz, _1, _2, _3));
+ this, _1, _2, _3));
}
else
{
- LLAvatarNameCache::get(id,
- boost::bind(&LLPostponedNotification::onAvatarNameCache,
- thiz, _1, _2));
+ fetchAvatarName(id);
}
}
@@ -1804,9 +1849,24 @@ void LLPostponedNotification::onGroupNameCache(const LLUUID& id,
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
diff --git a/indra/llui/llnotifications.h b/indra/llui/llnotifications.h
index d7534c416d..87573c2a56 100644
--- a/indra/llui/llnotifications.h
+++ b/indra/llui/llnotifications.h
@@ -87,17 +87,16 @@
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/type_traits.hpp>
+#include <boost/signals2.hpp>
-// we want to minimize external dependencies, but this one is important
-#include "llsd.h"
-
-// and we need this to manage the notification callbacks
#include "llevents.h"
#include "llfunctorregistry.h"
-#include "llpointer.h"
#include "llinitparam.h"
-#include "llnotificationslistener.h"
+#include "llmortician.h"
#include "llnotificationptr.h"
+#include "llpointer.h"
+#include "llrefcount.h"
+#include "llsdparam.h"
class LLAvatarName;
typedef enum e_notification_priority
@@ -164,6 +163,7 @@ public:
struct FormElementBase : public LLInitParam::Block<FormElementBase>
{
Optional<std::string> name;
+ Optional<bool> enabled;
FormElementBase();
};
@@ -233,16 +233,21 @@ public:
} 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);
- bool hasElement(const std::string& element_name);
- void addElement(const std::string& type, const std::string& name, const LLSD& value = LLSD());
+ 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);
@@ -296,42 +301,52 @@ LOG_CLASS(LLNotification);
friend class LLNotifications;
public:
+
// parameter object used to instantiate a new notification
struct Params : public LLInitParam::Block<Params>
{
friend class LLNotification;
Mandatory<std::string> name;
-
- // optional
- Optional<LLSD> substitutions;
- Optional<LLSD> payload;
+ Optional<LLUUID> id;
+ Optional<LLSD> substitutions,
+ form_elements,
+ payload;
Optional<ENotificationPriority, NotificationPriorityValues> priority;
- Optional<LLSD> form_elements;
- Optional<LLDate> time_stamp;
+ Optional<LLDate> time_stamp,
+ expiry;
Optional<LLNotificationContext*> context;
Optional<void*> responder;
+ Optional<bool> offer_from_agent;
+ Optional<bool> is_dnd;
struct Functor : public LLInitParam::ChoiceBlock<Functor>
{
Alternative<std::string> name;
Alternative<LLNotificationFunctorRegistry::ResponseFunctor> function;
Alternative<LLNotificationResponderPtr> responder;
+ Alternative<LLSD> responder_sd;
Functor()
- : name("functor_name"),
+ : name("responseFunctor"),
function("functor"),
- responder("responder")
+ responder("responder"),
+ responder_sd("responder_sd")
{}
};
Optional<Functor> functor;
Params()
: name("name"),
+ id("id"),
priority("priority", NOTIFICATION_PRIORITY_UNSPECIFIED),
- time_stamp("time_stamp"),
+ time_stamp("time"),
payload("payload"),
- form_elements("form_elements")
+ 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;
@@ -340,9 +355,13 @@ public:
Params(const std::string& _name)
: name("name"),
priority("priority", NOTIFICATION_PRIORITY_UNSPECIFIED),
- time_stamp("time_stamp"),
+ time_stamp("time"),
payload("payload"),
- form_elements("form_elements")
+ form_elements("form"),
+ substitutions("substitutions"),
+ expiry("expiry"),
+ offer_from_agent("offer_from_agent", false),
+ is_dnd("is_dnd", false)
{
functor.name = _name;
name = _name;
@@ -355,7 +374,7 @@ public:
private:
- LLUUID mId;
+ const LLUUID mId;
LLSD mPayload;
LLSD mSubstitutions;
LLDate mTimestamp;
@@ -367,8 +386,9 @@ private:
ENotificationPriority mPriority;
LLNotificationFormPtr mForm;
void* mResponderObj; // TODO - refactor/remove this field
- bool mIsReusable;
LLNotificationResponderPtr mResponder;
+ bool mOfferFromAgent;
+ bool mIsDND;
// a reference to the template
LLNotificationTemplatePtr mTemplatep;
@@ -392,18 +412,10 @@ private:
void init(const std::string& template_name, const LLSD& form_elements);
- LLNotification(const Params& p);
-
- // this is just for making it easy to look things up in a set organized by UUID -- DON'T USE IT
- // for anything real!
- LLNotification(LLUUID uuid) : mId(uuid), mCancelled(false), mRespondedTo(false), mIgnored(false), mPriority(NOTIFICATION_PRIORITY_UNSPECIFIED), mTemporaryResponder(false) {}
-
void cancel();
public:
-
- // constructor from a saved notification
- LLNotification(const LLSD& sd);
+ LLNotification(const LLSDParamAdapter<Params>& p);
void setResponseFunctor(std::string const &responseFunctorName);
@@ -446,7 +458,12 @@ public:
// ["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();
+ LLSD asLLSD(bool excludeTemplateElements = false);
+
+ const LLNotificationFormPtr getForm();
+ void updateForm(const LLNotificationFormPtr& form);
+
+ void repost();
void respond(const LLSD& sd);
void respondWithDefault();
@@ -507,6 +524,21 @@ public:
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;
@@ -514,8 +546,21 @@ public:
std::string getURL() const;
S32 getURLOption() const;
S32 getURLOpenExternally() const;
+ bool canLogToChat() const;
+ bool canLogToIM() const;
+ bool canShowToast() const;
+ bool hasFormElements() const;
+ void playSound();
+
+ typedef enum e_combine_behavior
+ {
+ REPLACE_WITH_NEW,
+ KEEP_OLD,
+ CANCEL_OLD
+
+ } ECombineBehavior;
- const LLNotificationFormPtr getForm();
+ ECombineBehavior getCombineBehavior() const;
const LLDate getExpiration() const
{
@@ -532,10 +577,6 @@ public:
return mId;
}
- bool isReusable() { return mIsReusable; }
-
- void setReusable(bool reusable) { mIsReusable = reusable; }
-
// comparing two notifications normally means comparing them by UUID (so we can look them
// up quickly this way)
bool operator<(const LLNotification& rhs) const
@@ -647,44 +688,17 @@ namespace LLNotificationFilters
namespace LLNotificationComparators
{
- typedef enum e_direction { ORDER_DECREASING, ORDER_INCREASING } EDirection;
-
- // generic order functor that takes method or member variable reference
- template<typename T>
- struct orderBy
+ struct orderByUUID
{
- typedef boost::function<T (LLNotificationPtr)> field_t;
- orderBy(field_t field, EDirection direction = ORDER_INCREASING) : mField(field), mDirection(direction) {}
bool operator()(LLNotificationPtr lhs, LLNotificationPtr rhs)
{
- if (mDirection == ORDER_DECREASING)
- {
- return mField(lhs) > mField(rhs);
- }
- else
- {
- return mField(lhs) < mField(rhs);
- }
+ return lhs->id() < rhs->id();
}
-
- field_t mField;
- EDirection mDirection;
- };
-
- struct orderByUUID : public orderBy<const LLUUID&>
- {
- orderByUUID(EDirection direction = ORDER_INCREASING) : orderBy<const LLUUID&>(&LLNotification::id, direction) {}
- };
-
- struct orderByDate : public orderBy<const LLDate&>
- {
- orderByDate(EDirection direction = ORDER_INCREASING) : orderBy<const LLDate&>(&LLNotification::getDate, direction) {}
};
};
typedef boost::function<bool (LLNotificationPtr)> LLNotificationFilter;
-typedef boost::function<bool (LLNotificationPtr, LLNotificationPtr)> LLNotificationComparator;
-typedef std::set<LLNotificationPtr, LLNotificationComparator> LLNotificationSet;
+typedef std::set<LLNotificationPtr, LLNotificationComparators::orderByUUID> LLNotificationSet;
typedef std::multimap<std::string, LLNotificationPtr> LLNotificationMap;
// ========================================================
@@ -705,12 +719,14 @@ typedef std::multimap<std::string, LLNotificationPtr> LLNotificationMap;
// all of the built-in tests should attach to the "Visible" channel
//
class LLNotificationChannelBase :
- public LLEventTrackable
+ public LLEventTrackable,
+ public LLRefCount
{
LOG_CLASS(LLNotificationChannelBase);
public:
- LLNotificationChannelBase(LLNotificationFilter filter, LLNotificationComparator comp) :
- mFilter(filter), mItems(comp)
+ LLNotificationChannelBase(LLNotificationFilter filter)
+ : mFilter(filter),
+ mItems()
{}
virtual ~LLNotificationChannelBase() {}
// you can also connect to a Channel, so you can be notified of
@@ -776,6 +792,9 @@ protected:
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;
};
@@ -785,64 +804,53 @@ protected:
// 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::shared_ptr<LLNotificationChannel> LLNotificationChannelPtr;
+typedef boost::intrusive_ptr<LLNotificationChannel> 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 shared_ptr to manage it.
-//
-// NOTE: LLNotificationChannel is self-registering. The *correct* way to create one is to
-// do something like:
-// LLNotificationChannel::buildChannel("name", "parent"...);
-// This returns an LLNotificationChannelPtr, which you can store, or
-// you can then retrieve the channel by using the registry:
-// LLNotifications::instance().getChannel("name")...
+// make it inherit from boost::noncopyable, and then create a map of LLPointer to manage it.
//
class LLNotificationChannel :
boost::noncopyable,
- public LLNotificationChannelBase
+ public LLNotificationChannelBase,
+ public LLInstanceTracker<LLNotificationChannel, std::string>
{
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<Params>
+ {
+ Mandatory<std::string> name;
+ Optional<LLNotificationFilter> filter;
+ Multiple<std::string> 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; }
- std::string getParentChannelName() { return mParent; }
+
+ void connectToChannel(const std::string& channel_name);
bool isEmpty() const;
S32 size() const;
Iterator begin();
Iterator end();
+ size_t size();
- // Channels have a comparator to control sort order;
- // the default sorts by arrival date
- void setComparator(LLNotificationComparator comparator);
-
std::string summarize();
- // factory method for constructing these channels; since they're self-registering,
- // we want to make sure that you can't use new to make them
- static LLNotificationChannelPtr buildChannel(const std::string& name, const std::string& parent,
- LLNotificationFilter filter=LLNotificationFilters::includeEverything,
- LLNotificationComparator comparator=LLNotificationComparators::orderByUUID());
-
-protected:
- // Notification Channels have a filter, which determines which notifications
- // will be added to this channel.
- // Channel filters cannot change.
- // Channels have a protected constructor so you can't make smart pointers that don't
- // come from our internal reference; call NotificationChannel::build(args)
- LLNotificationChannel(const std::string& name, const std::string& parent,
- LLNotificationFilter filter, LLNotificationComparator comparator);
-
private:
std::string mName;
std::string mParent;
- LLNotificationComparator mComparator;
};
// An interface class to provide a clean linker seam to the LLNotifications class.
@@ -925,10 +933,6 @@ public:
void createDefaultChannels();
- typedef std::map<std::string, LLNotificationChannelPtr> ChannelMap;
- ChannelMap mChannels;
-
- void addChannel(LLNotificationChannelPtr pChan);
LLNotificationChannelPtr getChannel(const std::string& channelName);
std::string getGlobalString(const std::string& key) const;
@@ -966,7 +970,7 @@ private:
bool mIgnoreAllNotifications;
- boost::scoped_ptr<LLNotificationsListener> mListener;
+ std::vector<LLNotificationChannelPtr> mDefaultChannels;
};
/**
@@ -979,7 +983,7 @@ private:
* 1 create class derived from LLPostponedNotification;
* 2 call LLPostponedNotification::add method;
*/
-class LLPostponedNotification
+class LLPostponedNotification : public LLMortician
{
public:
/**
@@ -997,26 +1001,38 @@ public:
thiz->mParams = params;
// Avoid header file dependency on llcachename.h
- lookupName(thiz, id, is_group);
+ thiz->lookupName(id, is_group);
}
private:
- static void lookupName(LLPostponedNotification* thiz, const LLUUID& id, bool is_group);
+ 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()
{
- delete this;
+ die();
}
protected:
- LLPostponedNotification() {}
- virtual ~LLPostponedNotification() {}
+ LLPostponedNotification()
+ : mParams(),
+ mName(),
+ mAvatarNameCacheConnection()
+ {}
+
+ virtual ~LLPostponedNotification()
+ {
+ if (mAvatarNameCacheConnection.connected())
+ {
+ mAvatarNameCacheConnection.disconnect();
+ }
+ }
/**
* Abstract method provides possibility to modify notification parameters and
@@ -1027,6 +1043,58 @@ protected:
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", &notificationFilter)
+ {
+ }
+
+ typedef std::vector<LLNotificationPtr> 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);
+ }
+
+ std::vector<LLNotificationPtr> mHistory;
};
#endif//LL_LLNOTIFICATIONS_H
diff --git a/indra/llui/llnotificationslistener.cpp b/indra/llui/llnotificationslistener.cpp
deleted file mode 100644
index 3bbeb3a778..0000000000
--- a/indra/llui/llnotificationslistener.cpp
+++ /dev/null
@@ -1,354 +0,0 @@
-/**
- * @file llnotificationslistener.cpp
- * @author Brad Kittenbrink
- * @date 2009-07-08
- * @brief Implementation for llnotificationslistener.
- *
- * $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 "llnotificationslistener.h"
-#include "llnotifications.h"
-#include "llnotificationtemplate.h"
-#include "llsd.h"
-#include "llui.h"
-
-LLNotificationsListener::LLNotificationsListener(LLNotifications & notifications) :
- LLEventAPI("LLNotifications",
- "LLNotifications listener to (e.g.) pop up a notification"),
- mNotifications(notifications)
-{
- add("requestAdd",
- "Add a notification with specified [\"name\"], [\"substitutions\"] and [\"payload\"].\n"
- "If optional [\"reply\"] specified, arrange to send user response on that LLEventPump.",
- &LLNotificationsListener::requestAdd);
- add("listChannels",
- "Post to [\"reply\"] a map of info on existing channels",
- &LLNotificationsListener::listChannels,
- LLSD().with("reply", LLSD()));
- add("listChannelNotifications",
- "Post to [\"reply\"] an array of info on notifications in channel [\"channel\"]",
- &LLNotificationsListener::listChannelNotifications,
- LLSD().with("reply", LLSD()).with("channel", LLSD()));
- add("respond",
- "Respond to notification [\"uuid\"] with data in [\"response\"]",
- &LLNotificationsListener::respond,
- LLSD().with("uuid", LLSD()));
- add("cancel",
- "Cancel notification [\"uuid\"]",
- &LLNotificationsListener::cancel,
- LLSD().with("uuid", LLSD()));
- add("ignore",
- "Ignore future notification [\"name\"]\n"
- "(from <notification name= > in notifications.xml)\n"
- "according to boolean [\"ignore\"].\n"
- "If [\"name\"] is omitted or undefined, [un]ignore all future notifications.\n"
- "Note that ignored notifications are not forwarded unless intercepted before\n"
- "the \"Ignore\" channel.",
- &LLNotificationsListener::ignore);
- add("forward",
- "Forward to [\"pump\"] future notifications on channel [\"channel\"]\n"
- "according to boolean [\"forward\"]. When enabled, only types matching\n"
- "[\"types\"] are forwarded, as follows:\n"
- "omitted or undefined: forward all notifications\n"
- "string: forward only the specific named [sig]type\n"
- "array of string: forward any notification matching any named [sig]type.\n"
- "When boolean [\"respond\"] is true, we auto-respond to each forwarded\n"
- "notification.",
- &LLNotificationsListener::forward,
- LLSD().with("channel", LLSD()));
-}
-
-// This is here in the .cpp file so we don't need the definition of class
-// Forwarder in the header file.
-LLNotificationsListener::~LLNotificationsListener()
-{
-}
-
-void LLNotificationsListener::requestAdd(const LLSD& event_data) const
-{
- if(event_data.has("reply"))
- {
- mNotifications.add(event_data["name"],
- event_data["substitutions"],
- event_data["payload"],
- boost::bind(&LLNotificationsListener::NotificationResponder,
- this,
- event_data["reply"].asString(),
- _1, _2
- )
- );
- }
- else
- {
- mNotifications.add(event_data["name"],
- event_data["substitutions"],
- event_data["payload"]);
- }
-}
-
-void LLNotificationsListener::NotificationResponder(const std::string& reply_pump,
- const LLSD& notification,
- const LLSD& response) const
-{
- LLSD reponse_event;
- reponse_event["notification"] = notification;
- reponse_event["response"] = response;
- LLEventPumps::getInstance()->obtain(reply_pump).post(reponse_event);
-}
-
-void LLNotificationsListener::listChannels(const LLSD& params) const
-{
- LLReqID reqID(params);
- LLSD response(reqID.makeResponse());
- for (LLNotifications::ChannelMap::const_iterator cmi(mNotifications.mChannels.begin()),
- cmend(mNotifications.mChannels.end());
- cmi != cmend; ++cmi)
- {
- LLSD channelInfo;
- channelInfo["parent"] = cmi->second->getParentChannelName();
- response[cmi->first] = channelInfo;
- }
- LLEventPumps::instance().obtain(params["reply"]).post(response);
-}
-
-void LLNotificationsListener::listChannelNotifications(const LLSD& params) const
-{
- LLReqID reqID(params);
- LLSD response(reqID.makeResponse());
- LLNotificationChannelPtr channel(mNotifications.getChannel(params["channel"]));
- if (channel)
- {
- LLSD notifications(LLSD::emptyArray());
- for (LLNotificationChannel::Iterator ni(channel->begin()), nend(channel->end());
- ni != nend; ++ni)
- {
- notifications.append(asLLSD(*ni));
- }
- response["notifications"] = notifications;
- }
- LLEventPumps::instance().obtain(params["reply"]).post(response);
-}
-
-void LLNotificationsListener::respond(const LLSD& params) const
-{
- LLNotificationPtr notification(mNotifications.find(params["uuid"]));
- if (notification)
- {
- notification->respond(params["response"]);
- }
-}
-
-void LLNotificationsListener::cancel(const LLSD& params) const
-{
- LLNotificationPtr notification(mNotifications.find(params["uuid"]));
- if (notification)
- {
- mNotifications.cancel(notification);
- }
-}
-
-void LLNotificationsListener::ignore(const LLSD& params) const
-{
- // Calling a method named "ignore", but omitting its "ignore" Boolean
- // argument, should by default cause something to be ignored. Explicitly
- // pass ["ignore"] = false to cancel ignore.
- bool ignore = true;
- if (params.has("ignore"))
- {
- ignore = params["ignore"].asBoolean();
- }
- // This method can be used to affect either a single notification name or
- // all future notifications. The two use substantially different mechanisms.
- if (params["name"].isDefined())
- {
- // ["name"] was passed: ignore just that notification
- LLNotificationTemplatePtr templatep = mNotifications.getTemplate(params["name"]);
- if (templatep)
- {
- templatep->mForm->setIgnored(ignore);
- }
- }
- else
- {
- // no ["name"]: ignore all future notifications
- mNotifications.setIgnoreAllNotifications(ignore);
- }
-}
-
-class LLNotificationsListener::Forwarder: public LLEventTrackable
-{
- LOG_CLASS(LLNotificationsListener::Forwarder);
-public:
- Forwarder(LLNotifications& llnotifications, const std::string& channel):
- mNotifications(llnotifications),
- mRespond(false)
- {
- // Connect to the specified channel on construction. Because
- // LLEventTrackable is a base, we should automatically disconnect when
- // destroyed.
- LLNotificationChannelPtr channelptr(llnotifications.getChannel(channel));
- if (channelptr)
- {
- // Insert our processing as a "passed filter" listener. This way
- // we get to run before all the "changed" listeners, and we get to
- // swipe it (hide it from the other listeners) if desired.
- channelptr->connectPassedFilter(boost::bind(&Forwarder::handle, this, _1));
- }
- }
-
- void setPumpName(const std::string& name) { mPumpName = name; }
- void setTypes(const LLSD& types) { mTypes = types; }
- void setRespond(bool respond) { mRespond = respond; }
-
-private:
- bool handle(const LLSD& notification) const;
- bool matchType(const LLSD& filter, const std::string& type) const;
-
- LLNotifications& mNotifications;
- std::string mPumpName;
- LLSD mTypes;
- bool mRespond;
-};
-
-void LLNotificationsListener::forward(const LLSD& params)
-{
- std::string channel(params["channel"]);
- // First decide whether we're supposed to start forwarding or stop it.
- // Default to true.
- bool forward = true;
- if (params.has("forward"))
- {
- forward = params["forward"].asBoolean();
- }
- if (! forward)
- {
- // This is a request to stop forwarding notifications on the specified
- // channel. The rest of the params don't matter.
- // Because mForwarders contains scoped_ptrs, erasing the map entry
- // DOES delete the heap Forwarder object. Because Forwarder derives
- // from LLEventTrackable, destroying it disconnects it from the
- // channel.
- mForwarders.erase(channel);
- return;
- }
- // From here on, we know we're being asked to start (or modify) forwarding
- // on the specified channel. Find or create an appropriate Forwarder.
- ForwarderMap::iterator
- entry(mForwarders.insert(ForwarderMap::value_type(channel, ForwarderMap::mapped_type())).first);
- if (! entry->second)
- {
- entry->second.reset(new Forwarder(mNotifications, channel));
- }
- // Now, whether this Forwarder is brand-new or not, update it with the new
- // request info.
- Forwarder& fwd(*entry->second);
- fwd.setPumpName(params["pump"]);
- fwd.setTypes(params["types"]);
- fwd.setRespond(params["respond"]);
-}
-
-bool LLNotificationsListener::Forwarder::handle(const LLSD& notification) const
-{
- LL_INFOS("LLNotificationsListener") << "handle(" << notification << ")" << LL_ENDL;
- if (notification["sigtype"].asString() == "delete")
- {
- LL_INFOS("LLNotificationsListener") << "ignoring delete" << LL_ENDL;
- // let other listeners see the "delete" operation
- return false;
- }
- LLNotificationPtr note(mNotifications.find(notification["id"]));
- if (! note)
- {
- LL_INFOS("LLNotificationsListener") << notification["id"] << " not found" << LL_ENDL;
- return false;
- }
- if (! matchType(mTypes, note->getType()))
- {
- LL_INFOS("LLNotificationsListener") << "didn't match types " << mTypes << LL_ENDL;
- // We're not supposed to intercept this particular notification. Let
- // other listeners process it.
- return false;
- }
- LL_INFOS("LLNotificationsListener") << "sending via '" << mPumpName << "'" << LL_ENDL;
- // This is a notification we care about. Forward it through specified
- // LLEventPump.
- LLEventPumps::instance().obtain(mPumpName).post(asLLSD(note));
- // Are we also being asked to auto-respond?
- if (mRespond)
- {
- LL_INFOS("LLNotificationsListener") << "should respond" << LL_ENDL;
- note->respond(LLSD::emptyMap());
- // Did that succeed in removing the notification? Only cancel() if
- // it's still around -- otherwise we get an LL_ERRS crash!
- note = mNotifications.find(notification["id"]);
- if (note)
- {
- LL_INFOS("LLNotificationsListener") << "respond() didn't clear, canceling" << LL_ENDL;
- mNotifications.cancel(note);
- }
- }
- // If we've auto-responded to this notification, then it's going to be
- // deleted. Other listeners would get the change operation, try to look it
- // up and be baffled by lookup failure. So when we auto-respond, suppress
- // this notification: don't pass it to other listeners.
- return mRespond;
-}
-
-bool LLNotificationsListener::Forwarder::matchType(const LLSD& filter, const std::string& type) const
-{
- // Decide whether this notification matches filter:
- // undefined: forward all notifications
- if (filter.isUndefined())
- {
- return true;
- }
- // array of string: forward any notification matching any named type
- if (filter.isArray())
- {
- for (LLSD::array_const_iterator ti(filter.beginArray()), tend(filter.endArray());
- ti != tend; ++ti)
- {
- if (ti->asString() == type)
- {
- return true;
- }
- }
- // Didn't match any entry in the array
- return false;
- }
- // string: forward only the specific named type
- return (filter.asString() == type);
-}
-
-LLSD LLNotificationsListener::asLLSD(LLNotificationPtr note)
-{
- LLSD notificationInfo(note->asLLSD());
- // For some reason the following aren't included in LLNotification::asLLSD().
- notificationInfo["summary"] = note->summarize();
- notificationInfo["id"] = note->id();
- notificationInfo["type"] = note->getType();
- notificationInfo["message"] = note->getMessage();
- notificationInfo["label"] = note->getLabel();
- return notificationInfo;
-}
diff --git a/indra/llui/llnotificationslistener.h b/indra/llui/llnotificationslistener.h
deleted file mode 100644
index f9f7641de6..0000000000
--- a/indra/llui/llnotificationslistener.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/**
- * @file llnotificationslistener.h
- * @author Brad Kittenbrink
- * @date 2009-07-08
- * @brief Wrap subset of LLNotifications API in event API for test scripts.
- *
- * $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_LLNOTIFICATIONSLISTENER_H
-#define LL_LLNOTIFICATIONSLISTENER_H
-
-#include "lleventapi.h"
-#include "llnotificationptr.h"
-#include <boost/shared_ptr.hpp>
-#include <map>
-#include <string>
-
-class LLNotifications;
-class LLSD;
-
-class LLNotificationsListener : public LLEventAPI
-{
-public:
- LLNotificationsListener(LLNotifications & notifications);
- ~LLNotificationsListener();
-
-private:
- void requestAdd(LLSD const & event_data) const;
-
- void NotificationResponder(const std::string& replypump,
- const LLSD& notification,
- const LLSD& response) const;
-
- void listChannels(const LLSD& params) const;
- void listChannelNotifications(const LLSD& params) const;
- void respond(const LLSD& params) const;
- void cancel(const LLSD& params) const;
- void ignore(const LLSD& params) const;
- void forward(const LLSD& params);
-
- static LLSD asLLSD(LLNotificationPtr);
-
- class Forwarder;
- typedef std::map<std::string, boost::shared_ptr<Forwarder> > ForwarderMap;
- ForwarderMap mForwarders;
- LLNotifications & mNotifications;
-};
-
-#endif // LL_LLNOTIFICATIONSLISTENER_H
diff --git a/indra/llui/llnotificationtemplate.h b/indra/llui/llnotificationtemplate.h
index b3b0bae862..18a82190b5 100644
--- a/indra/llui/llnotificationtemplate.h
+++ b/indra/llui/llnotificationtemplate.h
@@ -49,7 +49,6 @@
//#include "llfunctorregistry.h"
//#include "llpointer.h"
#include "llinitparam.h"
-//#include "llnotificationslistener.h"
//#include "llnotificationptr.h"
//#include "llcachename.h"
#include "llnotifications.h"
@@ -61,6 +60,18 @@ typedef boost::shared_ptr<LLNotificationForm> LLNotificationFormPtr;
// from the appropriate local language directory).
struct LLNotificationTemplate
{
+ struct CombineBehaviorNames
+ : public LLInitParam::TypeValuesHelper<LLNotification::ECombineBehavior, CombineBehaviorNames>
+ {
+ static void declareValues()
+ {
+ declare("replace_with_new", LLNotification::REPLACE_WITH_NEW);
+ declare("keep_old", LLNotification::KEEP_OLD);
+ declare("cancel_old", LLNotification::CANCEL_OLD);
+ }
+ };
+
+
struct GlobalString : public LLInitParam::Block<GlobalString>
{
Mandatory<std::string> name,
@@ -94,9 +105,11 @@ struct LLNotificationTemplate
Optional<LLInitParam::Flag> dummy_val;
public:
Multiple<UniquenessContext> contexts;
+ Optional<LLNotification::ECombineBehavior, CombineBehaviorNames> combine;
UniquenessConstraint()
: contexts("context"),
+ combine("combine", LLNotification::REPLACE_WITH_NEW),
dummy_val("")
{}
};
@@ -183,7 +196,10 @@ struct LLNotificationTemplate
struct Params : public LLInitParam::Block<Params>
{
Mandatory<std::string> name;
- Optional<bool> persist;
+ Optional<bool> persist,
+ log_to_im,
+ show_toast,
+ log_to_chat;
Optional<std::string> functor,
icon,
label,
@@ -204,6 +220,9 @@ struct LLNotificationTemplate
Params()
: name("name"),
persist("persist", false),
+ log_to_im("log_to_im", false),
+ show_toast("show_toast", true),
+ log_to_chat("log_to_chat", true),
functor("functor"),
icon("icon"),
label("label"),
@@ -262,6 +281,7 @@ struct LLNotificationTemplate
// (used for things like progress indications, or repeating warnings
// like "the grid is going down in N minutes")
bool mUnique;
+ LLNotification::ECombineBehavior mCombineBehavior;
// if we want to be unique only if a certain part of the payload or substitutions args
// are constant specify the field names for the payload. The notification will only be
// combined if all of the fields named in the context are identical in the
@@ -302,12 +322,15 @@ struct LLNotificationTemplate
LLNotificationFormPtr mForm;
// default priority for notifications of this type
ENotificationPriority mPriority;
- // UUID of the audio file to be played when this notification arrives
- // this is loaded as a name, but looked up to get the UUID upon template load.
- // If null, it wasn't specified.
- LLUUID mSoundEffect;
+ // Stores the sound name which can then be used to play the sound using make_ui_sound
+ std::string mSoundName;
// List of tags that rules can match against.
std::list<std::string> mTags;
+
+ // inject these notifications into chat/IM streams
+ bool mLogToChat;
+ bool mLogToIM;
+ bool mShowToast;
};
#endif //LL_LLNOTIFICATION_TEMPLATE_H
diff --git a/indra/llui/llresizebar.cpp b/indra/llui/llresizebar.cpp
index ba90fa5e0c..15e56cbfe5 100644
--- a/indra/llui/llresizebar.cpp
+++ b/indra/llui/llresizebar.cpp
@@ -45,7 +45,8 @@ LLResizeBar::LLResizeBar(const LLResizeBar::Params& p)
mSide( p.side ),
mSnappingEnabled(p.snapping_enabled),
mAllowDoubleClickSnapping(p.allow_double_click_snapping),
- mResizingView(p.resizing_view)
+ mResizingView(p.resizing_view),
+ mResizeListener(NULL)
{
setFollowsNone();
// set up some generically good follow code.
@@ -300,6 +301,11 @@ BOOL LLResizeBar::handleHover(S32 x, S32 y, MASK mask)
}
}
+ if (mResizeListener)
+ {
+ mResizeListener(NULL);
+ }
+
return handled;
} // end LLResizeBar::handleHover
diff --git a/indra/llui/llresizebar.h b/indra/llui/llresizebar.h
index 6daf191918..8190a95a71 100644
--- a/indra/llui/llresizebar.h
+++ b/indra/llui/llresizebar.h
@@ -71,6 +71,7 @@ public:
void setEnableSnapping(BOOL enable) { mSnappingEnabled = enable; }
void setAllowDoubleClickSnapping(BOOL allow) { mAllowDoubleClickSnapping = allow; }
bool canResize() { return getEnabled() && mMaxSize > mMinSize; }
+ void setResizeListener(boost::function<void(void*)> listener) {mResizeListener = listener;}
private:
S32 mDragLastScreenX;
@@ -84,6 +85,7 @@ private:
BOOL mSnappingEnabled;
BOOL mAllowDoubleClickSnapping;
LLView* mResizingView;
+ boost::function<void(void*)> mResizeListener;
};
#endif // LL_RESIZEBAR_H
diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp
index d332aa933e..8b9fb47d5c 100644
--- a/indra/llui/llscrolllistctrl.cpp
+++ b/indra/llui/llscrolllistctrl.cpp
@@ -1841,7 +1841,7 @@ void LLScrollListCtrl::copyNameToClipboard(std::string id, bool is_group)
{
LLAvatarName av_name;
LLAvatarNameCache::get(LLUUID(id), &av_name);
- name = av_name.getLegacyName();
+ name = av_name.getAccountName();
}
LLUrlAction::copyURLToClipboard(name);
}
diff --git a/indra/llui/llspinctrl.cpp b/indra/llui/llspinctrl.cpp
index 934879cdfd..8a728df2e7 100644
--- a/indra/llui/llspinctrl.cpp
+++ b/indra/llui/llspinctrl.cpp
@@ -52,6 +52,7 @@ 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"),
@@ -129,6 +130,10 @@ LLSpinCtrl::LLSpinCtrl(const LLSpinCtrl::Params& p)
params.follows.flags(FOLLOWS_LEFT | FOLLOWS_BOTTOM);
mEditor = LLUICtrlFactory::create<LLLineEditor> (params);
mEditor->setFocusReceivedCallback( boost::bind(&LLSpinCtrl::onEditorGainFocus, _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
diff --git a/indra/llui/llspinctrl.h b/indra/llui/llspinctrl.h
index 87814f838e..e34add879d 100644
--- a/indra/llui/llspinctrl.h
+++ b/indra/llui/llspinctrl.h
@@ -44,6 +44,7 @@ public:
Optional<S32> label_width;
Optional<U32> decimal_digits;
Optional<bool> allow_text_entry;
+ Optional<bool> allow_digits_only;
Optional<bool> label_wrap;
Optional<LLUIColor> text_enabled_color;
diff --git a/indra/llui/lltabcontainer.cpp b/indra/llui/lltabcontainer.cpp
index 5fc2cc350d..0c43a571b8 100644
--- a/indra/llui/lltabcontainer.cpp
+++ b/indra/llui/lltabcontainer.cpp
@@ -506,8 +506,8 @@ void LLTabContainer::draw()
}
}
- mPrevArrowBtn->setFlashing(FALSE);
- mNextArrowBtn->setFlashing(FALSE);
+ mPrevArrowBtn->setFlashing(false);
+ mNextArrowBtn->setFlashing(false);
}
@@ -1209,7 +1209,11 @@ void LLTabContainer::removeTabPanel(LLPanel* child)
update_images(mTabList[mTabList.size()-2], mLastTabParams, getTabPosition());
}
- removeChild( tuple->mButton );
+ if (!getTabsHidden())
+ {
+ // We need to remove tab buttons only if the tabs are not hidden.
+ removeChild( tuple->mButton );
+ }
delete tuple->mButton;
removeChild( tuple->mTabPanel );
@@ -1479,6 +1483,8 @@ BOOL LLTabContainer::setTab(S32 which)
for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
{
LLTabTuple* tuple = *iter;
+ if (!tuple)
+ continue;
BOOL is_selected = ( tuple == selected_tuple );
tuple->mButton->setUseEllipses(mUseTabEllipses);
tuple->mButton->setHAlign(mFontHalign);
diff --git a/indra/llui/lltabcontainer.h b/indra/llui/lltabcontainer.h
index cebace2ceb..57862fc626 100644
--- a/indra/llui/lltabcontainer.h
+++ b/indra/llui/lltabcontainer.h
@@ -188,10 +188,11 @@ public:
void selectFirstTab();
void selectLastTab();
void selectNextTab();
- void selectPrevTab();
+ 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);
@@ -242,8 +243,6 @@ private:
void setTabsHidden(BOOL hidden) { mTabsHidden = hidden; }
BOOL getTabsHidden() const { return mTabsHidden; }
-
- void setCurrentPanelIndex(S32 index) { mCurrentTabIdx = index; }
void scrollPrev() { mScrollPos = llmax(0, mScrollPos-1); } // No wrap
void scrollNext() { mScrollPos = llmin(mScrollPos+1, mMaxScrollPos); } // No wrap
diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp
index 3815eec447..4bb819a7f6 100644
--- a/indra/llui/lltextbase.cpp
+++ b/indra/llui/lltextbase.cpp
@@ -46,6 +46,7 @@
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),
@@ -145,6 +146,7 @@ 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"),
@@ -179,7 +181,7 @@ LLTextBase::LLTextBase(const LLTextBase::Params &p)
: LLUICtrl(p, LLTextViewModelPtr(new LLTextViewModel)),
mURLClickSignal(NULL),
mMaxTextByteLength( p.max_text_length ),
- mDefaultFont(p.font),
+ mFont(p.font),
mFontShadow(p.font_shadow),
mPopupMenu(NULL),
mReadOnly(p.read_only),
@@ -190,6 +192,7 @@ LLTextBase::LLTextBase(const LLTextBase::Params &p)
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),
@@ -319,21 +322,26 @@ bool LLTextBase::truncate()
return did_truncate;
}
-const LLStyle::Params& LLTextBase::getDefaultStyleParams()
+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)
{
- mDefaultStyle
+ mStyle
.color(LLUIColor(&mFgColor)) // pass linked color instead of copy of mFGColor
.readonly_color(LLUIColor(&mReadOnlyFgColor))
.selected_color(LLUIColor(&mTextSelectedColor))
- .font(mDefaultFont)
+ .font(mFont)
.drop_shadow(mFontShadow);
mStyleDirty = false;
}
- return mDefaultStyle;
+ return mStyle;
+}
+
+void LLTextBase::beforeValueChange()
+{
+
}
void LLTextBase::onValueChange(S32 start, S32 end)
@@ -522,11 +530,17 @@ void LLTextBase::drawCursor()
void LLTextBase::drawText()
{
- const S32 text_len = getLength();
- if( text_len <= 0 )
+ 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
@@ -592,7 +606,8 @@ void LLTextBase::drawText()
// Find the start of the first word
U32 word_start = seg_start, word_end = -1;
- while ( (word_start < wstrText.length()) && (!LLStringOps::isAlpha(wstrText[word_start])) )
+ U32 text_length = wstrText.length();
+ while ( (word_start < text_length) && (!LLStringOps::isAlpha(wstrText[word_start])) )
{
word_start++;
}
@@ -614,11 +629,15 @@ void LLTextBase::drawText()
break;
}
- // Don't process words shorter than 3 characters
- std::string word = wstring_to_utf8str(wstrText.substr(word_start, word_end - word_start));
- if ( (word.length() >= 3) && (!LLSpellChecker::instance().checkSpelling(word)) )
+ if (word_start < text_length && word_end <= text_length && word_end > word_start)
{
- mMisspellRanges.push_back(std::pair<U32, U32>(word_start, word_end));
+ 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<U32, U32>(word_start, word_end));
+ }
}
// Find the start of the next word
@@ -739,6 +758,8 @@ void LLTextBase::drawText()
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();
@@ -770,7 +791,7 @@ S32 LLTextBase::insertStringNoUndo(S32 pos, const LLWString &wstr, LLTextBase::s
else
{
// create default editable segment to hold new text
- LLStyleConstSP sp(new LLStyle(getDefaultStyleParams()));
+ LLStyleConstSP sp(new LLStyle(getStyleParams()));
default_segment = new LLNormalTextSegment( sp, pos, pos + insert_len, *this);
}
@@ -814,6 +835,8 @@ S32 LLTextBase::insertStringNoUndo(S32 pos, const LLWString &wstr, LLTextBase::s
S32 LLTextBase::removeStringNoUndo(S32 pos, S32 length)
{
+
+ beforeValueChange();
segment_set_t::iterator seg_iter = getSegIterContaining(pos);
while(seg_iter != mSegments.end())
{
@@ -872,6 +895,8 @@ S32 LLTextBase::removeStringNoUndo(S32 pos, S32 length)
S32 LLTextBase::overwriteCharNoUndo(S32 pos, llwchar wc)
{
+ beforeValueChange();
+
if (pos > (S32)getLength())
{
return 0;
@@ -890,7 +915,7 @@ void LLTextBase::createDefaultSegment()
// ensures that there is always at least one segment
if (mSegments.empty())
{
- LLStyleConstSP sp(new LLStyle(getDefaultStyleParams()));
+ LLStyleConstSP sp(new LLStyle(getStyleParams()));
LLTextSegmentPtr default_segment = new LLNormalTextSegment( sp, 0, getLength() + 1, *this);
mSegments.insert(default_segment);
default_segment->linkToDocument(this);
@@ -980,6 +1005,13 @@ void LLTextBase::insertSegment(LLTextSegmentPtr segment_to_insert)
BOOL LLTextBase::handleMouseDown(S32 x, S32 y, MASK mask)
{
+ // handle triple click
+ if (!mTripleClickTimer.hasExpired())
+ {
+ selectAll();
+ return TRUE;
+ }
+
LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y);
if (cur_segment && cur_segment->handleMouseDown(x, y, mask))
{
@@ -1054,6 +1086,7 @@ BOOL LLTextBase::handleRightMouseUp(S32 x, S32 y, MASK mask)
BOOL LLTextBase::handleDoubleClick(S32 x, S32 y, MASK mask)
{
+ mTripleClickTimer.setTimerExpirySec(TRIPLE_CLICK_INTERVAL);
LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y);
if (cur_segment && cur_segment->handleDoubleClick(x, y, mask))
{
@@ -1338,6 +1371,25 @@ void LLTextBase::onSpellCheckSettingsChange()
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()
{
@@ -1859,6 +1911,7 @@ void LLTextBase::createUrlContextMenu(S32 x, S32 y, const std::string &in_url)
registrar.add("Url.Execute", boost::bind(&LLUrlAction::executeSLURL, url));
registrar.add("Url.Teleport", boost::bind(&LLUrlAction::teleportToLocation, url));
registrar.add("Url.ShowProfile", boost::bind(&LLUrlAction::showProfile, 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));
@@ -1924,7 +1977,7 @@ static LLFastTimer::DeclareTimer FTM_PARSE_HTML("Parse HTML");
void LLTextBase::appendTextImpl(const std::string &new_text, const LLStyle::Params& input_params)
{
LLStyle::Params style_params(input_params);
- style_params.fillFrom(getDefaultStyleParams());
+ 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).
@@ -2009,6 +2062,44 @@ void LLTextBase::appendText(const std::string &new_text, bool prepend_newline, c
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()
+{
+ return !getLength() && !mLabel.empty() && !hasFocus();
+}
+
+void LLTextBase::setFont(const LLFontGL* font)
+{
+ mFont = font;
+ mStyleDirty = true;
+}
+
void LLTextBase::needsReflow(S32 index)
{
lldebugs << "reflow on object " << (void*)this << " index = " << mReflowIndex << ", new index = " << index << llendl;
@@ -2399,7 +2490,7 @@ LLRect LLTextBase::getLocalRectFromDocIndex(S32 pos) const
{
// return default height rect in upper left
local_rect = content_window_rect;
- local_rect.mBottom = local_rect.mTop - mDefaultFont->getLineHeight();
+ local_rect.mBottom = local_rect.mTop - mFont->getLineHeight();
return local_rect;
}
@@ -2904,7 +2995,7 @@ F32 LLNormalTextSegment::drawClippedSegment(S32 seg_start, S32 seg_end, S32 sele
{
F32 alpha = LLViewDrawContext::getCurrentContext().mAlpha;
- const LLWString &text = mEditor.getWText();
+ const LLWString &text = getWText();
F32 right_x = rect.mLeft;
if (!mStyle->isVisible())
@@ -3067,7 +3158,7 @@ bool LLNormalTextSegment::getDimensions(S32 first_char, S32 num_chars, S32& widt
if (num_chars > 0)
{
height = mFontHeight;
- const LLWString &text = mEditor.getWText();
+ const LLWString &text = getWText();
// if last character is a newline, then return true, forcing line break
width = mStyle->getFont()->getWidth(text.c_str(), mStart + first_char, num_chars);
}
@@ -3076,7 +3167,7 @@ bool LLNormalTextSegment::getDimensions(S32 first_char, S32 num_chars, S32& widt
S32 LLNormalTextSegment::getOffset(S32 segment_local_x_coord, S32 start_offset, S32 num_chars, bool round) const
{
- const LLWString &text = mEditor.getWText();
+ const LLWString &text = getWText();
return mStyle->getFont()->charFromPixelOffset(text.c_str(), mStart + start_offset,
(F32)segment_local_x_coord,
F32_MAX,
@@ -3086,7 +3177,7 @@ S32 LLNormalTextSegment::getOffset(S32 segment_local_x_coord, S32 start_offset,
S32 LLNormalTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars) const
{
- const LLWString &text = mEditor.getWText();
+ const LLWString &text = getWText();
LLUIImagePtr image = mStyle->getImage();
if( image.notNull())
@@ -3122,7 +3213,7 @@ S32 LLNormalTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 lin
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 >= mEditor.getLength() ))
+ && (last_char_in_run >= getLength()))
{
num_chars++;
}
@@ -3140,6 +3231,39 @@ void LLNormalTextSegment::dump() const
llendl;
}
+/*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();
+}
+
//
// LLOnHoverChangeableTextSegment
//
diff --git a/indra/llui/lltextbase.h b/indra/llui/lltextbase.h
index 90b147cee1..ad566a36d3 100644
--- a/indra/llui/lltextbase.h
+++ b/indra/llui/lltextbase.h
@@ -106,7 +106,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();
+ virtual ~LLNormalTextSegment();
/*virtual*/ bool getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height) const;
/*virtual*/ S32 getOffset(S32 segment_local_x_coord, S32 start_offset, S32 num_chars, bool round) const;
@@ -131,6 +131,9 @@ public:
protected:
F32 drawClippedSegment(S32 seg_start, S32 seg_end, S32 selection_start, S32 selection_end, LLRect rect);
+ virtual const LLWString& getWText() const;
+ virtual const S32 getLength() const;
+
protected:
class LLTextBase& mEditor;
LLStyleConstSP mStyle;
@@ -140,6 +143,21 @@ protected:
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 changes it's style depending of mouse pointer position ( is it inside or outside segment)
class LLOnHoverChangeableTextSegment : public LLNormalTextSegment
{
@@ -251,6 +269,7 @@ public:
Optional<LLUIColor> cursor_color,
text_color,
text_readonly_color,
+ text_tentative_color,
bg_readonly_color,
bg_writeable_color,
bg_focus_color,
@@ -314,6 +333,9 @@ public:
/*virtual*/ BOOL canDeselect() const;
/*virtual*/ void deselect();
+ virtual void onFocusReceived();
+ virtual void onFocusLost();
+
// LLSpellCheckMenuHandler overrides
/*virtual*/ bool getSpellCheck() const;
@@ -351,6 +373,21 @@ public:
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 );
+
+ const std::string& getLabel() { return mLabel.getString(); }
+ const LLWString& getWlabel() { return mLabel.getWString();}
+
+ /**
+ * 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);
@@ -390,7 +427,7 @@ public:
bool scrolledToStart();
bool scrolledToEnd();
- const LLFontGL* getDefaultFont() const { return mDefaultFont; }
+ const LLFontGL* getFont() const { return mFont; }
virtual void appendLineBreakSegment(const LLStyle::Params& style_params);
virtual void appendImageSegment(const LLStyle::Params& style_params);
@@ -464,7 +501,9 @@ protected:
LLTextBase(const Params &p);
virtual ~LLTextBase();
void initFromParams(const Params& p);
+ virtual void beforeValueChange();
virtual void onValueChange(S32 start, S32 end);
+ virtual bool useLabel();
// draw methods
void drawSelectionBackground(); // draws the black box behind the selected text
@@ -490,7 +529,7 @@ protected:
void createDefaultSegment();
virtual void updateSegments();
void insertSegment(LLTextSegmentPtr segment_to_insert);
- const LLStyle::Params& getDefaultStyleParams();
+ const LLStyle::Params& getStyleParams();
// manage lines
S32 getLineStart( S32 line ) const;
@@ -535,15 +574,16 @@ protected:
LLRect mTextBoundingRect;
// default text style
- LLStyle::Params mDefaultStyle;
+ LLStyle::Params mStyle;
bool mStyleDirty;
- const LLFontGL* const mDefaultFont; // font that is used when none specified, can only be set by constructor
- const LLFontGL::ShadowType mFontShadow; // shadow style, can only be set by constructor
+ const LLFontGL* mFont;
+ const LLFontGL::ShadowType mFontShadow;
// colors
LLUIColor mCursorColor;
LLUIColor mFgColor;
LLUIColor mReadOnlyFgColor;
+ LLUIColor mTentativeFgColor;
LLUIColor mWriteableBgColor;
LLUIColor mReadOnlyBgColor;
LLUIColor mFocusBgColor;
@@ -558,7 +598,8 @@ protected:
// selection
S32 mSelectionStart;
S32 mSelectionEnd;
-
+ LLTimer mTripleClickTimer;
+
BOOL mIsSelecting; // Are we in the middle of a drag-select?
// spell checking
@@ -587,6 +628,7 @@ protected:
bool mClip; // clip text to widget rect
bool mClipPartial; // false if we show lines that are partially inside bounding rect
bool mPlainText; // didn't use Image or Icon segments
+ bool mAutoIndent;
S32 mMaxTextByteLength; // Maximum length mText is allowed to be in bytes
// support widgets
@@ -602,6 +644,7 @@ protected:
// Fired when a URL link is clicked
commit_signal_t* mURLClickSignal;
+ LLUIString mLabel; // text label that is visible when no user text provided
};
#endif
diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp
index 46fbd1e6a0..2f120479d9 100644
--- a/indra/llui/lltexteditor.cpp
+++ b/indra/llui/lltexteditor.cpp
@@ -237,6 +237,7 @@ LLTextEditor::Params::Params()
embedded_items("embedded_items", false),
ignore_tab("ignore_tab", true),
show_line_numbers("show_line_numbers", false),
+ auto_indent("auto_indent", true),
default_color("default_color"),
commit_on_focus_lost("commit_on_focus_lost", false),
show_context_menu("show_context_menu"),
@@ -247,11 +248,13 @@ LLTextEditor::Params::Params()
LLTextEditor::LLTextEditor(const LLTextEditor::Params& p) :
LLTextBase(p),
+ mAutoreplaceCallback(),
mBaseDocIsPristine(TRUE),
mPristineCmd( NULL ),
mLastCmd( NULL ),
mDefaultColor( p.default_color() ),
mShowLineNumbers ( p.show_line_numbers ),
+ mAutoIndent(p.auto_indent),
mCommitOnFocusLost( p.commit_on_focus_lost),
mAllowEmbeddedItems( p.embedded_items ),
mMouseDownX(0),
@@ -260,7 +263,8 @@ LLTextEditor::LLTextEditor(const LLTextEditor::Params& p) :
mPrevalidateFunc(p.prevalidate_callback()),
mContextMenu(NULL),
mShowContextMenu(p.show_context_menu),
- mEnableTooltipPaste(p.enable_tooltip_paste)
+ mEnableTooltipPaste(p.enable_tooltip_paste),
+ mPassDelete(FALSE)
{
mSourceID.generate();
@@ -1096,7 +1100,25 @@ void LLTextEditor::addChar(llwchar wc)
}
setCursorPos(mCursorPos + addChar( mCursorPos, wc ));
+
+ 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::addLineBreakChar()
{
if( !getEnabled() )
@@ -1621,7 +1643,10 @@ BOOL LLTextEditor::handleSpecialKey(const KEY key, const MASK mask)
{
deleteSelection(FALSE);
}
- autoIndent(); // TODO: make this optional
+ if (mAutoIndent)
+ {
+ autoIndent();
+ }
}
else
{
@@ -1792,7 +1817,7 @@ BOOL LLTextEditor::handleUnicodeCharHere(llwchar uni_char)
// virtual
BOOL LLTextEditor::canDoDelete() const
{
- return !mReadOnly && ( hasSelection() || (mCursorPos < getLength()) );
+ return !mReadOnly && ( !mPassDelete || ( hasSelection() || (mCursorPos < getLength())) );
}
void LLTextEditor::doDelete()
@@ -2061,7 +2086,7 @@ void LLTextEditor::drawPreeditMarker()
return;
}
- const S32 line_height = mDefaultFont->getLineHeight();
+ const S32 line_height = mFont->getLineHeight();
S32 line_start = getLineStart(cur_line);
S32 line_y = mVisibleTextRect.mTop - line_height;
@@ -2100,16 +2125,16 @@ void LLTextEditor::drawPreeditMarker()
S32 preedit_left = mVisibleTextRect.mLeft;
if (left > line_start)
{
- preedit_left += mDefaultFont->getWidth(text, line_start, left - line_start);
+ preedit_left += mFont->getWidth(text, line_start, left - line_start);
}
S32 preedit_right = mVisibleTextRect.mLeft;
if (right < line_end)
{
- preedit_right += mDefaultFont->getWidth(text, line_start, right - line_start);
+ preedit_right += mFont->getWidth(text, line_start, right - line_start);
}
else
{
- preedit_right += mDefaultFont->getWidth(text, line_start, line_end - line_start);
+ preedit_right += mFont->getWidth(text, line_start, line_end - line_start);
}
if (mPreeditStandouts[i])
@@ -2784,11 +2809,11 @@ BOOL LLTextEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect
const LLWString textString(getWText());
const llwchar * const text = textString.c_str();
- const S32 line_height = mDefaultFont->getLineHeight();
+ const S32 line_height = mFont->getLineHeight();
if (coord)
{
- const S32 query_x = mVisibleTextRect.mLeft + mDefaultFont->getWidth(text, current_line_start, query - current_line_start);
+ 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);
@@ -2800,17 +2825,17 @@ BOOL LLTextEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect
S32 preedit_left = mVisibleTextRect.mLeft;
if (preedit_left_position > current_line_start)
{
- preedit_left += mDefaultFont->getWidth(text, current_line_start, 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 += mDefaultFont->getWidth(text, current_line_start, preedit_right_position - current_line_start);
+ preedit_right += mFont->getWidth(text, current_line_start, preedit_right_position - current_line_start);
}
else
{
- preedit_right += mDefaultFont->getWidth(text, current_line_start, current_line_end - current_line_start);
+ 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;
@@ -2887,7 +2912,7 @@ void LLTextEditor::markAsPreedit(S32 position, S32 length)
S32 LLTextEditor::getPreeditFontSize() const
{
- return llround((F32)mDefaultFont->getLineHeight() * LLUI::sGLScaleFactor.mV[VY]);
+ return llround((F32)mFont->getLineHeight() * LLUI::sGLScaleFactor.mV[VY]);
}
BOOL LLTextEditor::isDirty() const
diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h
index e60fe03e58..969e072704 100644
--- a/indra/llui/lltexteditor.h
+++ b/indra/llui/lltexteditor.h
@@ -65,7 +65,8 @@ public:
show_line_numbers,
commit_on_focus_lost,
show_context_menu,
- enable_tooltip_paste;
+ enable_tooltip_paste,
+ auto_indent;
//colors
Optional<LLUIColor> default_color;
@@ -157,6 +158,11 @@ public:
BOOL isPristine() const;
BOOL allowsEmbeddedItems() const { return mAllowEmbeddedItems; }
+ // Autoreplace (formerly part of LLLineEditor)
+ typedef boost::function<void(S32&, S32&, LLWString&, S32&, const LLWString&)> autoreplace_callback_t;
+ autoreplace_callback_t mAutoreplaceCallback;
+ void setAutoreplaceCallback(autoreplace_callback_t cb) { mAutoreplaceCallback = cb; }
+
//
// Text manipulation
//
@@ -203,6 +209,8 @@ public:
void setShowContextMenu(bool show) { mShowContextMenu = show; }
bool getShowContextMenu() const { return mShowContextMenu; }
+ void setPassDelete(BOOL b) { mPassDelete = b; }
+
protected:
void showContextMenu(S32 x, S32 y);
void drawPreeditMarker();
@@ -215,8 +223,8 @@ 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 handleSpecialKey(const KEY key, const MASK mask);
BOOL handleSelectionKey(const KEY key, const MASK mask);
BOOL handleControlKey(const KEY key, const MASK mask);
@@ -280,6 +288,7 @@ protected:
LLUIColor mDefaultColor;
BOOL mShowLineNumbers;
+ bool mAutoIndent;
/*virtual*/ void updateSegments();
void updateLinkSegments();
@@ -325,6 +334,7 @@ private:
bool mShowContextMenu;
bool mParseOnTheFly;
bool mEnableTooltipPaste;
+ bool mPassDelete;
LLUUID mSourceID;
diff --git a/indra/llui/lltoggleablemenu.h b/indra/llui/lltoggleablemenu.h
index 4717b0d0ba..dfe70cbf54 100644
--- a/indra/llui/lltoggleablemenu.h
+++ b/indra/llui/lltoggleablemenu.h
@@ -60,6 +60,8 @@ public:
// its visibility off.
bool toggleVisibility();
+ LLHandle<LLToggleableMenu> getHandle() { return getDerivedHandle<LLToggleableMenu>(); }
+
protected:
bool mClosedByButtonClick;
LLRect mButtonRect;
diff --git a/indra/llui/lltoolbar.cpp b/indra/llui/lltoolbar.cpp
index 63b7e452d2..b9256dd890 100644
--- a/indra/llui/lltoolbar.cpp
+++ b/indra/llui/lltoolbar.cpp
@@ -872,8 +872,15 @@ void LLToolBar::reshape(S32 width, S32 height, BOOL called_from_parent)
void LLToolBar::createButtons()
{
+ std::set<LLUUID> set_flashing;
+
BOOST_FOREACH(LLToolBarButton* button, mButtons)
{
+ if (button->getFlashTimer() && button->getFlashTimer()->isFlashingInProgress())
+ {
+ set_flashing.insert(button->getCommandId().uuid());
+ }
+
if (mButtonRemoveSignal)
{
(*mButtonRemoveSignal)(button);
@@ -896,6 +903,11 @@ void LLToolBar::createButtons()
{
(*mButtonAddSignal)(button);
}
+
+ if (set_flashing.find(button->getCommandId().uuid()) != set_flashing.end())
+ {
+ button->setFlashing(true);
+ }
}
mNeedsLayout = true;
}
@@ -920,6 +932,7 @@ LLToolBarButton* LLToolBar::createButton(const LLCommandId& id)
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<LLToolBarButton>(button_p);
diff --git a/indra/llui/lltooltip.cpp b/indra/llui/lltooltip.cpp
index 7f1566d64a..f52a3b3323 100644
--- a/indra/llui/lltooltip.cpp
+++ b/indra/llui/lltooltip.cpp
@@ -288,7 +288,7 @@ void LLToolTip::initFromParams(const LLToolTip::Params& p)
mTextBox->setText(p.message());
}
- S32 text_width = llmin(p.max_width(), mTextBox->getTextPixelWidth());
+ S32 text_width = llmin(p.max_width(), mTextBox->getTextPixelWidth() + 1);
S32 text_height = mTextBox->getTextPixelHeight();
mTextBox->reshape(text_width, text_height);
if (mInfoButton)
diff --git a/indra/llui/llui.cpp b/indra/llui/llui.cpp
index 6d2bc1837c..2a774d54a3 100644
--- a/indra/llui/llui.cpp
+++ b/indra/llui/llui.cpp
@@ -77,6 +77,7 @@ std::list<std::string> gUntranslated;
/*static*/ LLUI::settings_map_t LLUI::sSettingGroups;
/*static*/ LLImageProviderInterface* LLUI::sImageProvider = NULL;
/*static*/ LLUIAudioCallback LLUI::sAudioCallback = NULL;
+/*static*/ LLUIAudioCallback LLUI::sDeferredAudioCallback = NULL;
/*static*/ LLVector2 LLUI::sGLScaleFactor(1.f, 1.f);
/*static*/ LLWindow* LLUI::sWindow = NULL;
/*static*/ LLView* LLUI::sRootView = NULL;
@@ -101,16 +102,18 @@ static LLDefaultChildRegistry::Register<LLToolBar> register_toolbar("toolbar");
//
// Functions
//
-void make_ui_sound(const char* namep)
+
+LLUUID find_ui_sound(const char * namep)
{
std::string name = ll_safe_string(namep);
+ LLUUID uuid = LLUUID(NULL);
if (!LLUI::sSettingGroups["config"]->controlExists(name))
{
llwarns << "tried to make UI sound for unknown sound name: " << name << llendl;
}
else
{
- LLUUID uuid(LLUI::sSettingGroups["config"]->getString(name));
+ uuid = LLUUID(LLUI::sSettingGroups["config"]->getString(name));
if (uuid.isNull())
{
if (LLUI::sSettingGroups["config"]->getString(name) == LLUUID::null.asString())
@@ -124,7 +127,6 @@ void make_ui_sound(const char* namep)
{
llwarns << "UI sound named: " << name << " does not translate to a valid uuid" << llendl;
}
-
}
else if (LLUI::sAudioCallback != NULL)
{
@@ -132,9 +134,28 @@ void make_ui_sound(const char* namep)
{
llinfos << "UI sound name: " << name << llendl;
}
- LLUI::sAudioCallback(uuid);
}
}
+
+ return uuid;
+}
+
+void make_ui_sound(const char* namep)
+{
+ LLUUID soundUUID = find_ui_sound(namep);
+ if(soundUUID.notNull())
+ {
+ LLUI::sAudioCallback(soundUUID);
+ }
+}
+
+void make_ui_sound_deferred(const char* namep)
+{
+ LLUUID soundUUID = find_ui_sound(namep);
+ if(soundUUID.notNull())
+ {
+ LLUI::sDeferredAudioCallback(soundUUID);
+ }
}
BOOL ui_point_in_rect(S32 x, S32 y, S32 left, S32 top, S32 right, S32 bottom)
@@ -978,31 +999,31 @@ void gl_rect_2d_checkerboard(const LLRect& rect, GLfloat alpha)
{
if (!LLGLSLShader::sNoFixedFunction)
{
- // Initialize the first time this is called.
- const S32 PIXELS = 32;
- static GLubyte checkerboard[PIXELS * PIXELS];
- static BOOL first = TRUE;
- if( first )
+ // Initialize the first time this is called.
+ const S32 PIXELS = 32;
+ static GLubyte checkerboard[PIXELS * PIXELS];
+ static BOOL first = TRUE;
+ if( first )
+ {
+ for( S32 i = 0; i < PIXELS; i++ )
{
- for( S32 i = 0; i < PIXELS; i++ )
+ for( S32 j = 0; j < PIXELS; j++ )
{
- for( S32 j = 0; j < PIXELS; j++ )
- {
- checkerboard[i * PIXELS + j] = ((i & 1) ^ (j & 1)) * 0xFF;
- }
+ checkerboard[i * PIXELS + j] = ((i & 1) ^ (j & 1)) * 0xFF;
}
- first = FALSE;
}
+ first = FALSE;
+ }
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
- // ...white squares
- gGL.color4f( 1.f, 1.f, 1.f, alpha );
- gl_rect_2d(rect);
+ // ...white squares
+ gGL.color4f( 1.f, 1.f, 1.f, alpha );
+ gl_rect_2d(rect);
- // ...gray squares
- gGL.color4f( .7f, .7f, .7f, alpha );
- gGL.flush();
+ // ...gray squares
+ gGL.color4f( .7f, .7f, .7f, alpha );
+ gGL.flush();
glPolygonStipple( checkerboard );
@@ -1478,148 +1499,137 @@ void gl_segmented_rect_2d_fragment_tex(const S32 left,
gGL.popUIMatrix();
}
-void gl_segmented_rect_3d_tex(const LLVector2& border_scale, const LLVector3& border_width,
- const LLVector3& border_height, const LLVector3& width_vec, const LLVector3& height_vec,
- const U32 edges)
+void gl_segmented_rect_3d_tex(const LLRectf& clip_rect, const LLRectf& center_uv_rect, const LLRectf& center_draw_rect,
+ const LLVector3& width_vec, const LLVector3& height_vec)
{
- LLVector3 left_border_width = ((edges & (~(U32)ROUNDED_RECT_RIGHT)) != 0) ? border_width : LLVector3::zero;
- LLVector3 right_border_width = ((edges & (~(U32)ROUNDED_RECT_LEFT)) != 0) ? border_width : LLVector3::zero;
-
- LLVector3 top_border_height = ((edges & (~(U32)ROUNDED_RECT_BOTTOM)) != 0) ? border_height : LLVector3::zero;
- LLVector3 bottom_border_height = ((edges & (~(U32)ROUNDED_RECT_TOP)) != 0) ? border_height : LLVector3::zero;
-
-
gGL.begin(LLRender::QUADS);
{
// draw bottom left
- gGL.texCoord2f(0.f, 0.f);
+ gGL.texCoord2f(clip_rect.mLeft, clip_rect.mBottom);
gGL.vertex3f(0.f, 0.f, 0.f);
- gGL.texCoord2f(border_scale.mV[VX], 0.f);
- gGL.vertex3fv(left_border_width.mV);
+ gGL.texCoord2f(center_uv_rect.mLeft, clip_rect.mBottom);
+ gGL.vertex3fv((center_draw_rect.mLeft * width_vec).mV);
- gGL.texCoord2f(border_scale.mV[VX], border_scale.mV[VY]);
- gGL.vertex3fv((left_border_width + bottom_border_height).mV);
+ gGL.texCoord2f(center_uv_rect.mLeft, center_uv_rect.mBottom);
+ gGL.vertex3fv((center_draw_rect.mLeft * width_vec + center_draw_rect.mBottom * height_vec).mV);
- gGL.texCoord2f(0.f, border_scale.mV[VY]);
- gGL.vertex3fv(bottom_border_height.mV);
+ gGL.texCoord2f(clip_rect.mLeft, center_uv_rect.mBottom);
+ gGL.vertex3fv((center_draw_rect.mBottom * height_vec).mV);
// draw bottom middle
- gGL.texCoord2f(border_scale.mV[VX], 0.f);
- gGL.vertex3fv(left_border_width.mV);
+ gGL.texCoord2f(center_uv_rect.mLeft, clip_rect.mBottom);
+ gGL.vertex3fv((center_draw_rect.mLeft * width_vec).mV);
- gGL.texCoord2f(1.f - border_scale.mV[VX], 0.f);
- gGL.vertex3fv((width_vec - right_border_width).mV);
+ gGL.texCoord2f(center_uv_rect.mRight, clip_rect.mBottom);
+ gGL.vertex3fv((center_draw_rect.mRight * width_vec).mV);
- gGL.texCoord2f(1.f - border_scale.mV[VX], border_scale.mV[VY]);
- gGL.vertex3fv((width_vec - right_border_width + bottom_border_height).mV);
+ gGL.texCoord2f(center_uv_rect.mRight, center_uv_rect.mBottom);
+ gGL.vertex3fv((center_draw_rect.mRight * width_vec + center_draw_rect.mBottom * height_vec).mV);
- gGL.texCoord2f(border_scale.mV[VX], border_scale.mV[VY]);
- gGL.vertex3fv((left_border_width + bottom_border_height).mV);
+ gGL.texCoord2f(center_uv_rect.mLeft, center_uv_rect.mBottom);
+ gGL.vertex3fv((center_draw_rect.mLeft * width_vec + center_draw_rect.mBottom * height_vec).mV);
// draw bottom right
- gGL.texCoord2f(1.f - border_scale.mV[VX], 0.f);
- gGL.vertex3fv((width_vec - right_border_width).mV);
+ gGL.texCoord2f(center_uv_rect.mRight, clip_rect.mBottom);
+ gGL.vertex3fv((center_draw_rect.mRight * width_vec).mV);
- gGL.texCoord2f(1.f, 0.f);
+ gGL.texCoord2f(clip_rect.mRight, clip_rect.mBottom);
gGL.vertex3fv(width_vec.mV);
- gGL.texCoord2f(1.f, border_scale.mV[VY]);
- gGL.vertex3fv((width_vec + bottom_border_height).mV);
+ gGL.texCoord2f(clip_rect.mRight, center_uv_rect.mBottom);
+ gGL.vertex3fv((width_vec + center_draw_rect.mBottom * height_vec).mV);
- gGL.texCoord2f(1.f - border_scale.mV[VX], border_scale.mV[VY]);
- gGL.vertex3fv((width_vec - right_border_width + bottom_border_height).mV);
+ gGL.texCoord2f(center_uv_rect.mRight, center_uv_rect.mBottom);
+ gGL.vertex3fv((center_draw_rect.mRight * width_vec + center_draw_rect.mBottom * height_vec).mV);
// draw left
- gGL.texCoord2f(0.f, border_scale.mV[VY]);
- gGL.vertex3fv(bottom_border_height.mV);
+ gGL.texCoord2f(clip_rect.mLeft, center_uv_rect.mBottom);
+ gGL.vertex3fv((center_draw_rect.mBottom * height_vec).mV);
- gGL.texCoord2f(border_scale.mV[VX], border_scale.mV[VY]);
- gGL.vertex3fv((left_border_width + bottom_border_height).mV);
+ gGL.texCoord2f(center_uv_rect.mLeft, center_uv_rect.mBottom);
+ gGL.vertex3fv((center_draw_rect.mLeft * width_vec + center_draw_rect.mBottom * height_vec).mV);
- gGL.texCoord2f(border_scale.mV[VX], 1.f - border_scale.mV[VY]);
- gGL.vertex3fv((left_border_width + height_vec - top_border_height).mV);
+ gGL.texCoord2f(center_uv_rect.mLeft, center_uv_rect.mTop);
+ gGL.vertex3fv((center_draw_rect.mLeft * width_vec + center_draw_rect.mTop * height_vec).mV);
- gGL.texCoord2f(0.f, 1.f - border_scale.mV[VY]);
- gGL.vertex3fv((height_vec - top_border_height).mV);
+ gGL.texCoord2f(clip_rect.mLeft, center_uv_rect.mTop);
+ gGL.vertex3fv((center_draw_rect.mTop * height_vec).mV);
// draw middle
- gGL.texCoord2f(border_scale.mV[VX], border_scale.mV[VY]);
- gGL.vertex3fv((left_border_width + bottom_border_height).mV);
+ gGL.texCoord2f(center_uv_rect.mLeft, center_uv_rect.mBottom);
+ gGL.vertex3fv((center_draw_rect.mLeft * width_vec + center_draw_rect.mBottom * height_vec).mV);
- gGL.texCoord2f(1.f - border_scale.mV[VX], border_scale.mV[VY]);
- gGL.vertex3fv((width_vec - right_border_width + bottom_border_height).mV);
+ gGL.texCoord2f(center_uv_rect.mRight, center_uv_rect.mBottom);
+ gGL.vertex3fv((center_draw_rect.mRight * width_vec + center_draw_rect.mBottom * height_vec).mV);
- gGL.texCoord2f(1.f - border_scale.mV[VX], 1.f - border_scale.mV[VY]);
- gGL.vertex3fv((width_vec - right_border_width + height_vec - top_border_height).mV);
+ gGL.texCoord2f(center_uv_rect.mRight, center_uv_rect.mTop);
+ gGL.vertex3fv((center_draw_rect.mRight * width_vec + center_draw_rect.mTop * height_vec).mV);
- gGL.texCoord2f(border_scale.mV[VX], 1.f - border_scale.mV[VY]);
- gGL.vertex3fv((left_border_width + height_vec - top_border_height).mV);
+ gGL.texCoord2f(center_uv_rect.mLeft, center_uv_rect.mTop);
+ gGL.vertex3fv((center_draw_rect.mLeft * width_vec + center_draw_rect.mTop * height_vec).mV);
// draw right
- gGL.texCoord2f(1.f - border_scale.mV[VX], border_scale.mV[VY]);
- gGL.vertex3fv((width_vec - right_border_width + bottom_border_height).mV);
+ gGL.texCoord2f(center_uv_rect.mRight, center_uv_rect.mBottom);
+ gGL.vertex3fv((center_draw_rect.mRight * width_vec + center_draw_rect.mBottom * height_vec).mV);
- gGL.texCoord2f(1.f, border_scale.mV[VY]);
- gGL.vertex3fv((width_vec + bottom_border_height).mV);
+ gGL.texCoord2f(clip_rect.mRight, center_uv_rect.mBottom);
+ gGL.vertex3fv((width_vec + center_draw_rect.mBottom * height_vec).mV);
- gGL.texCoord2f(1.f, 1.f - border_scale.mV[VY]);
- gGL.vertex3fv((width_vec + height_vec - top_border_height).mV);
+ gGL.texCoord2f(clip_rect.mRight, center_uv_rect.mTop);
+ gGL.vertex3fv((width_vec + center_draw_rect.mTop * height_vec).mV);
- gGL.texCoord2f(1.f - border_scale.mV[VX], 1.f - border_scale.mV[VY]);
- gGL.vertex3fv((width_vec - right_border_width + height_vec - top_border_height).mV);
+ gGL.texCoord2f(center_uv_rect.mRight, center_uv_rect.mTop);
+ gGL.vertex3fv((center_draw_rect.mRight * width_vec + center_draw_rect.mTop * height_vec).mV);
// draw top left
- gGL.texCoord2f(0.f, 1.f - border_scale.mV[VY]);
- gGL.vertex3fv((height_vec - top_border_height).mV);
+ gGL.texCoord2f(clip_rect.mLeft, center_uv_rect.mTop);
+ gGL.vertex3fv((center_draw_rect.mTop * height_vec).mV);
- gGL.texCoord2f(border_scale.mV[VX], 1.f - border_scale.mV[VY]);
- gGL.vertex3fv((left_border_width + height_vec - top_border_height).mV);
+ gGL.texCoord2f(center_uv_rect.mLeft, center_uv_rect.mTop);
+ gGL.vertex3fv((center_draw_rect.mLeft * width_vec + center_draw_rect.mTop * height_vec).mV);
- gGL.texCoord2f(border_scale.mV[VX], 1.f);
- gGL.vertex3fv((left_border_width + height_vec).mV);
+ gGL.texCoord2f(center_uv_rect.mLeft, clip_rect.mTop);
+ gGL.vertex3fv((center_draw_rect.mLeft * width_vec + height_vec).mV);
- gGL.texCoord2f(0.f, 1.f);
+ gGL.texCoord2f(clip_rect.mLeft, clip_rect.mTop);
gGL.vertex3fv((height_vec).mV);
// draw top middle
- gGL.texCoord2f(border_scale.mV[VX], 1.f - border_scale.mV[VY]);
- gGL.vertex3fv((left_border_width + height_vec - top_border_height).mV);
+ gGL.texCoord2f(center_uv_rect.mLeft, center_uv_rect.mTop);
+ gGL.vertex3fv((center_draw_rect.mLeft * width_vec + center_draw_rect.mTop * height_vec).mV);
- gGL.texCoord2f(1.f - border_scale.mV[VX], 1.f - border_scale.mV[VY]);
- gGL.vertex3fv((width_vec - right_border_width + height_vec - top_border_height).mV);
+ gGL.texCoord2f(center_uv_rect.mRight, center_uv_rect.mTop);
+ gGL.vertex3fv((center_draw_rect.mRight * width_vec + center_draw_rect.mTop * height_vec).mV);
- gGL.texCoord2f(1.f - border_scale.mV[VX], 1.f);
- gGL.vertex3fv((width_vec - right_border_width + height_vec).mV);
+ gGL.texCoord2f(center_uv_rect.mRight, clip_rect.mTop);
+ gGL.vertex3fv((center_draw_rect.mRight * width_vec + height_vec).mV);
- gGL.texCoord2f(border_scale.mV[VX], 1.f);
- gGL.vertex3fv((left_border_width + height_vec).mV);
+ gGL.texCoord2f(center_uv_rect.mLeft, clip_rect.mTop);
+ gGL.vertex3fv((center_draw_rect.mLeft * width_vec + height_vec).mV);
// draw top right
- gGL.texCoord2f(1.f - border_scale.mV[VX], 1.f - border_scale.mV[VY]);
- gGL.vertex3fv((width_vec - right_border_width + height_vec - top_border_height).mV);
+ gGL.texCoord2f(center_uv_rect.mRight, center_uv_rect.mTop);
+ gGL.vertex3fv((center_draw_rect.mRight * width_vec + center_draw_rect.mTop * height_vec).mV);
- gGL.texCoord2f(1.f, 1.f - border_scale.mV[VY]);
- gGL.vertex3fv((width_vec + height_vec - top_border_height).mV);
+ gGL.texCoord2f(clip_rect.mRight, center_uv_rect.mTop);
+ gGL.vertex3fv((width_vec + center_draw_rect.mTop * height_vec).mV);
- gGL.texCoord2f(1.f, 1.f);
+ gGL.texCoord2f(clip_rect.mRight, clip_rect.mTop);
gGL.vertex3fv((width_vec + height_vec).mV);
- gGL.texCoord2f(1.f - border_scale.mV[VX], 1.f);
- gGL.vertex3fv((width_vec - right_border_width + height_vec).mV);
+ gGL.texCoord2f(center_uv_rect.mRight, clip_rect.mTop);
+ gGL.vertex3fv((center_draw_rect.mRight * width_vec + height_vec).mV);
}
gGL.end();
}
-void gl_segmented_rect_3d_tex_top(const LLVector2& border_scale, const LLVector3& border_width, const LLVector3& border_height, const LLVector3& width_vec, const LLVector3& height_vec)
-{
- gl_segmented_rect_3d_tex(border_scale, border_width, border_height, width_vec, height_vec, ROUNDED_RECT_TOP);
-}
void LLUI::initClass(const settings_map_t& settings,
LLImageProviderInterface* image_provider,
LLUIAudioCallback audio_callback,
+ LLUIAudioCallback deferred_audio_callback,
const LLVector2* scale_factor,
const std::string& language)
{
@@ -1634,6 +1644,7 @@ void LLUI::initClass(const settings_map_t& settings,
sImageProvider = image_provider;
sAudioCallback = audio_callback;
+ sDeferredAudioCallback = deferred_audio_callback;
sGLScaleFactor = (scale_factor == NULL) ? LLVector2(1.f, 1.f) : *scale_factor;
sWindow = NULL; // set later in startup
LLFontGL::sShadowColor = LLUIColorTable::instance().getColor("ColorDropShadow");
@@ -2067,7 +2078,7 @@ const LLView* LLUI::resolvePath(const LLView* context, const std::string& path)
namespace LLInitParam
{
- ParamValue<LLUIColor, TypeValues<LLUIColor> >::ParamValue(const LLUIColor& color)
+ ParamValue<LLUIColor>::ParamValue(const LLUIColor& color)
: super_t(color),
red("red"),
green("green"),
@@ -2078,7 +2089,7 @@ namespace LLInitParam
updateBlockFromValue(false);
}
- void ParamValue<LLUIColor, TypeValues<LLUIColor> >::updateValueFromBlock()
+ void ParamValue<LLUIColor>::updateValueFromBlock()
{
if (control.isProvided() && !control().empty())
{
@@ -2090,7 +2101,7 @@ namespace LLInitParam
}
}
- void ParamValue<LLUIColor, TypeValues<LLUIColor> >::updateBlockFromValue(bool make_block_authoritative)
+ void ParamValue<LLUIColor>::updateBlockFromValue(bool make_block_authoritative)
{
LLColor4 color = getValue();
red.set(color.mV[VRED], make_block_authoritative);
@@ -2106,7 +2117,7 @@ namespace LLInitParam
&& !(b->getFontDesc() < a->getFontDesc());
}
- ParamValue<const LLFontGL*, TypeValues<const LLFontGL*> >::ParamValue(const LLFontGL* fontp)
+ ParamValue<const LLFontGL*>::ParamValue(const LLFontGL* fontp)
: super_t(fontp),
name("name"),
size("size"),
@@ -2120,7 +2131,7 @@ namespace LLInitParam
updateBlockFromValue(false);
}
- void ParamValue<const LLFontGL*, TypeValues<const LLFontGL*> >::updateValueFromBlock()
+ void ParamValue<const LLFontGL*>::updateValueFromBlock()
{
const LLFontGL* res_fontp = LLFontGL::getFontByName(name);
if (res_fontp)
@@ -2143,7 +2154,7 @@ namespace LLInitParam
}
}
- void ParamValue<const LLFontGL*, TypeValues<const LLFontGL*> >::updateBlockFromValue(bool make_block_authoritative)
+ void ParamValue<const LLFontGL*>::updateBlockFromValue(bool make_block_authoritative)
{
if (getValue())
{
@@ -2153,7 +2164,7 @@ namespace LLInitParam
}
}
- ParamValue<LLRect, TypeValues<LLRect> >::ParamValue(const LLRect& rect)
+ ParamValue<LLRect>::ParamValue(const LLRect& rect)
: super_t(rect),
left("left"),
top("top"),
@@ -2165,7 +2176,7 @@ namespace LLInitParam
updateBlockFromValue(false);
}
- void ParamValue<LLRect, TypeValues<LLRect> >::updateValueFromBlock()
+ void ParamValue<LLRect>::updateValueFromBlock()
{
LLRect rect;
@@ -2229,7 +2240,7 @@ namespace LLInitParam
updateValue(rect);
}
- void ParamValue<LLRect, TypeValues<LLRect> >::updateBlockFromValue(bool make_block_authoritative)
+ void ParamValue<LLRect>::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
@@ -2246,7 +2257,7 @@ namespace LLInitParam
height.set(value.getHeight(), make_block_authoritative);
}
- ParamValue<LLCoordGL, TypeValues<LLCoordGL> >::ParamValue(const LLCoordGL& coord)
+ ParamValue<LLCoordGL>::ParamValue(const LLCoordGL& coord)
: super_t(coord),
x("x"),
y("y")
@@ -2254,12 +2265,12 @@ namespace LLInitParam
updateBlockFromValue(false);
}
- void ParamValue<LLCoordGL, TypeValues<LLCoordGL> >::updateValueFromBlock()
+ void ParamValue<LLCoordGL>::updateValueFromBlock()
{
updateValue(LLCoordGL(x, y));
}
- void ParamValue<LLCoordGL, TypeValues<LLCoordGL> >::updateBlockFromValue(bool make_block_authoritative)
+ void ParamValue<LLCoordGL>::updateBlockFromValue(bool make_block_authoritative)
{
x.set(getValue().mX, make_block_authoritative);
y.set(getValue().mY, make_block_authoritative);
diff --git a/indra/llui/llui.h b/indra/llui/llui.h
index c5a12d2b31..4c1703392a 100644
--- a/indra/llui/llui.h
+++ b/indra/llui/llui.h
@@ -62,6 +62,7 @@ class LLHelp;
// UI colors
extern const LLColor4 UI_VERTEX_COLOR;
void make_ui_sound(const char* name);
+void make_ui_sound_deferred(const char * name);
BOOL ui_point_in_rect(S32 x, S32 y, S32 left, S32 top, S32 right, S32 bottom);
void gl_state_for_2d(S32 width, S32 height);
@@ -127,8 +128,7 @@ typedef enum e_rounded_edge
void gl_segmented_rect_2d_tex(const S32 left, const S32 top, const S32 right, const S32 bottom, const S32 texture_width, const S32 texture_height, const S32 border_size, const U32 edges = ROUNDED_RECT_ALL);
void gl_segmented_rect_2d_fragment_tex(const S32 left, const S32 top, const S32 right, const S32 bottom, const S32 texture_width, const S32 texture_height, const S32 border_size, const F32 start_fragment, const F32 end_fragment, const U32 edges = ROUNDED_RECT_ALL);
-void gl_segmented_rect_3d_tex(const LLVector2& border_scale, const LLVector3& border_width, const LLVector3& border_height, const LLVector3& width_vec, const LLVector3& height_vec, U32 edges = ROUNDED_RECT_ALL);
-void gl_segmented_rect_3d_tex_top(const LLVector2& border_scale, const LLVector3& border_width, const LLVector3& border_height, const LLVector3& width_vec, const LLVector3& height_vec);
+void gl_segmented_rect_3d_tex(const LLRectf& clip_rect, const LLRectf& center_uv_rect, const LLRectf& center_draw_rect, const LLVector3& width_vec, const LLVector3& height_vec);
inline void gl_rect_2d( const LLRect& rect, BOOL filled )
{
@@ -275,6 +275,7 @@ public:
static void initClass(const settings_map_t& settings,
LLImageProviderInterface* image_provider,
LLUIAudioCallback audio_callback = NULL,
+ LLUIAudioCallback deferred_audio_callback = NULL,
const LLVector2 *scale_factor = NULL,
const std::string& language = LLStringUtil::null);
static void cleanupClass();
@@ -360,6 +361,7 @@ public:
//
static settings_map_t sSettingGroups;
static LLUIAudioCallback sAudioCallback;
+ static LLUIAudioCallback sDeferredAudioCallback;
static LLVector2 sGLScaleFactor;
static LLWindow* sWindow;
static LLView* sRootView;
@@ -507,7 +509,7 @@ public:
namespace LLInitParam
{
template<>
- class ParamValue<LLRect, TypeValues<LLRect> >
+ class ParamValue<LLRect>
: public CustomParamValue<LLRect>
{
typedef CustomParamValue<LLRect> super_t;
@@ -526,7 +528,7 @@ namespace LLInitParam
};
template<>
- class ParamValue<LLUIColor, TypeValues<LLUIColor> >
+ class ParamValue<LLUIColor>
: public CustomParamValue<LLUIColor>
{
typedef CustomParamValue<LLUIColor> super_t;
@@ -544,7 +546,7 @@ namespace LLInitParam
};
template<>
- class ParamValue<const LLFontGL*, TypeValues<const LLFontGL*> >
+ class ParamValue<const LLFontGL*>
: public CustomParamValue<const LLFontGL* >
{
typedef CustomParamValue<const LLFontGL*> super_t;
@@ -584,7 +586,7 @@ namespace LLInitParam
template<>
- class ParamValue<LLCoordGL, TypeValues<LLCoordGL> >
+ class ParamValue<LLCoordGL>
: public CustomParamValue<LLCoordGL>
{
typedef CustomParamValue<LLCoordGL> super_t;
diff --git a/indra/llui/lluictrlfactory.cpp b/indra/llui/lluictrlfactory.cpp
index bd06476936..60fee47ae0 100644
--- a/indra/llui/lluictrlfactory.cpp
+++ b/indra/llui/lluictrlfactory.cpp
@@ -247,13 +247,13 @@ const LLInitParam::BaseBlock& get_empty_param_block()
// 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& tag)
+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_tag = LLWidgetNameRegistry::instance().getValue(param_block_type);
- if (existing_tag != NULL)
+ std::string* existing_name = LLWidgetNameRegistry::instance().getValue(param_block_type);
+ if (existing_name != NULL)
{
- if(*existing_tag != tag)
+ 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
@@ -262,19 +262,15 @@ void LLUICtrlFactory::registerWidget(const std::type_info* widget_type, const st
}
else
{
- // widget already registered
+ // widget already registered this name
return;
}
}
- LLWidgetNameRegistry::instance().defaultRegistrar().add(param_block_type, tag);
+
+ 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<T>);
}
-//static
-const std::string* LLUICtrlFactory::getWidgetTag(const std::type_info* widget_type)
-{
- return LLWidgetNameRegistry::instance().getValue(widget_type);
-}
diff --git a/indra/llui/lluictrlfactory.h b/indra/llui/lluictrlfactory.h
index f6971261d7..876bb5ef46 100644
--- a/indra/llui/lluictrlfactory.h
+++ b/indra/llui/lluictrlfactory.h
@@ -98,7 +98,7 @@ private:
ParamDefaults()
{
// look up template file for this param block...
- const std::string* param_block_tag = getWidgetTag(&typeid(PARAM_BLOCK));
+ const std::string* param_block_tag = LLWidgetNameRegistry::instance().getValue(&typeid(PARAM_BLOCK));
if (param_block_tag)
{ // ...and if it exists, back fill values using the most specific template first
PARAM_BLOCK params;
@@ -132,7 +132,6 @@ public:
template<typename T>
static const typename T::Params& getDefaultParams()
{
- //#pragma message("Generating ParamDefaults")
return ParamDefaults<typename T::Params, 0>::instance().get();
}
@@ -285,8 +284,6 @@ private:
}
- static const std::string* getWidgetTag(const std::type_info* widget_type);
-
// this exists to get around dependency on llview
static void setCtrlParent(LLView* view, LLView* parent, S32 tab_group);
diff --git a/indra/llui/lluiimage.cpp b/indra/llui/lluiimage.cpp
index 1d9ce29ba9..9ed98f941f 100644
--- a/indra/llui/lluiimage.cpp
+++ b/indra/llui/lluiimage.cpp
@@ -112,6 +112,50 @@ void LLUIImage::drawBorder(S32 x, S32 y, S32 width, S32 height, const LLColor4&
drawSolid(border_rect, color);
}
+void LLUIImage::draw3D(const LLVector3& origin_agent, const LLVector3& x_axis, const LLVector3& y_axis,
+ const LLRect& rect, const LLColor4& color)
+{
+ F32 border_scale = 1.f;
+ F32 border_height = (1.f - mScaleRegion.getHeight()) * getHeight();
+ F32 border_width = (1.f - mScaleRegion.getWidth()) * getWidth();
+ if (rect.getHeight() < border_height || rect.getWidth() < border_width)
+ {
+ if(border_height - rect.getHeight() > border_width - rect.getWidth())
+ {
+ border_scale = (F32)rect.getHeight() / border_height;
+ }
+ else
+ {
+ border_scale = (F32)rect.getWidth() / border_width;
+ }
+ }
+
+ LLUI::pushMatrix();
+ {
+ LLVector3 rect_origin = origin_agent + (rect.mLeft * x_axis) + (rect.mBottom * y_axis);
+ LLUI::translate(rect_origin.mV[VX],
+ rect_origin.mV[VY],
+ rect_origin.mV[VZ]);
+ gGL.getTexUnit(0)->bind(getImage());
+ gGL.color4fv(color.mV);
+
+ LLRectf center_uv_rect(mClipRegion.mLeft + mScaleRegion.mLeft * mClipRegion.getWidth(),
+ mClipRegion.mBottom + mScaleRegion.mTop * mClipRegion.getHeight(),
+ mClipRegion.mLeft + mScaleRegion.mRight * mClipRegion.getWidth(),
+ mClipRegion.mBottom + mScaleRegion.mBottom * mClipRegion.getHeight());
+ gl_segmented_rect_3d_tex(mClipRegion,
+ center_uv_rect,
+ LLRectf(border_width * border_scale * 0.5f / (F32)rect.getWidth(),
+ (rect.getHeight() - (border_height * border_scale * 0.5f)) / (F32)rect.getHeight(),
+ (rect.getWidth() - (border_width * border_scale * 0.5f)) / (F32)rect.getWidth(),
+ (border_height * border_scale * 0.5f) / (F32)rect.getHeight()),
+ rect.getWidth() * x_axis,
+ rect.getHeight() * y_axis);
+
+ } LLUI::popMatrix();
+}
+
+
S32 LLUIImage::getWidth() const
{
// return clipped dimensions of actual image area
@@ -155,7 +199,7 @@ void LLUIImage::onImageLoaded()
namespace LLInitParam
{
- void ParamValue<LLUIImage*, TypeValues<LLUIImage*> >::updateValueFromBlock()
+ void ParamValue<LLUIImage*>::updateValueFromBlock()
{
// The keyword "none" is specifically requesting a null image
// do not default to current value. Used to overwrite template images.
@@ -172,7 +216,7 @@ namespace LLInitParam
}
}
- void ParamValue<LLUIImage*, TypeValues<LLUIImage*> >::updateBlockFromValue(bool make_block_authoritative)
+ void ParamValue<LLUIImage*>::updateBlockFromValue(bool make_block_authoritative)
{
if (getValue() == NULL)
{
diff --git a/indra/llui/lluiimage.h b/indra/llui/lluiimage.h
index f07e8fa746..7817ba1c7b 100644
--- a/indra/llui/lluiimage.h
+++ b/indra/llui/lluiimage.h
@@ -64,7 +64,9 @@ public:
void drawBorder(S32 x, S32 y, S32 width, S32 height, const LLColor4& color, S32 border_width) const;
void drawBorder(const LLRect& rect, const LLColor4& color, S32 border_width) const { drawBorder(rect.mLeft, rect.mBottom, rect.getWidth(), rect.getHeight(), color, border_width); }
void drawBorder(S32 x, S32 y, const LLColor4& color, S32 border_width) const { drawBorder(x, y, getWidth(), getHeight(), color, border_width); }
-
+
+ void draw3D(const LLVector3& origin_agent, const LLVector3& x_axis, const LLVector3& y_axis, const LLRect& rect, const LLColor4& color);
+
const std::string& getName() const { return mName; }
virtual S32 getWidth() const;
@@ -92,7 +94,7 @@ protected:
namespace LLInitParam
{
template<>
- class ParamValue<LLUIImage*, TypeValues<LLUIImage*> >
+ class ParamValue<LLUIImage*>
: public CustomParamValue<LLUIImage*>
{
typedef boost::add_reference<boost::add_const<LLUIImage*>::type>::type T_const_ref;
@@ -100,7 +102,7 @@ namespace LLInitParam
public:
Optional<std::string> name;
- ParamValue(LLUIImage* const& image)
+ ParamValue(LLUIImage* const& image = NULL)
: super_t(image)
{
updateBlockFromValue(false);
diff --git a/indra/llui/llurlaction.cpp b/indra/llui/llurlaction.cpp
index fd9b3d9a6d..fd872eca4b 100644
--- a/indra/llui/llurlaction.cpp
+++ b/indra/llui/llurlaction.cpp
@@ -157,3 +157,17 @@ void LLUrlAction::showProfile(std::string url)
}
}
}
+
+void LLUrlAction::sendIM(std::string url)
+{
+ LLURI uri(url);
+ LLSD path_array = uri.pathArray();
+ if (path_array.size() == 4)
+ {
+ std::string id_str = path_array.get(2).asString();
+ if (LLUUID::validate(id_str))
+ {
+ executeSLURL("secondlife:///app/agent/" + id_str + "/im");
+ }
+ }
+}
diff --git a/indra/llui/llurlaction.h b/indra/llui/llurlaction.h
index c34960b826..f5f2ceba72 100644
--- a/indra/llui/llurlaction.h
+++ b/indra/llui/llurlaction.h
@@ -76,6 +76,7 @@ public:
/// if the Url specifies an SL command in the form like 'app/{cmd}/{id}/*', show its profile
static void showProfile(std::string url);
+ static void sendIM(std::string url);
/// specify the callbacks to enable this class's functionality
typedef boost::function<void (const std::string&)> url_callback_t;
diff --git a/indra/llui/llurlentry.cpp b/indra/llui/llurlentry.cpp
index a9e8fbb4e4..99ee688888 100644
--- a/indra/llui/llurlentry.cpp
+++ b/indra/llui/llurlentry.cpp
@@ -340,7 +340,8 @@ std::string LLUrlEntrySLURL::getLocation(const std::string &url) const
// 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()
+LLUrlEntryAgent::LLUrlEntryAgent() :
+ mAvatarNameCacheConnection()
{
mPattern = boost::regex(APP_HEADER_REGEX "/agent/[\\da-f-]+/\\w+",
boost::regex::perl|boost::regex::icase);
@@ -371,7 +372,9 @@ void LLUrlEntryAgent::callObservers(const std::string &id,
void LLUrlEntryAgent::onAvatarNameCache(const LLUUID& id,
const LLAvatarName& av_name)
{
- std::string label = av_name.getCompleteName();
+ mAvatarNameCacheConnection.disconnect();
+
+ std::string label = av_name.getCompleteName();
// received the agent name from the server - tell our observers
callObservers(id.asString(), label, mIcon);
@@ -456,9 +459,11 @@ std::string LLUrlEntryAgent::getLabel(const std::string &url, const LLUrlLabelCa
}
else
{
- LLAvatarNameCache::get(agent_id,
- boost::bind(&LLUrlEntryAgent::onAvatarNameCache,
- this, _1, _2));
+ if (mAvatarNameCacheConnection.connected())
+ {
+ mAvatarNameCacheConnection.disconnect();
+ }
+ mAvatarNameCacheConnection = LLAvatarNameCache::get(agent_id, boost::bind(&LLUrlEntryAgent::onAvatarNameCache, this, _1, _2));
addObserver(agent_id_string, url, cb);
return LLTrans::getString("LoadingData");
}
@@ -515,12 +520,15 @@ std::string LLUrlEntryAgent::getIcon(const std::string &url)
// 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()
+LLUrlEntryAgentName::LLUrlEntryAgentName() :
+ mAvatarNameCacheConnection()
{}
void LLUrlEntryAgentName::onAvatarNameCache(const LLUUID& id,
const LLAvatarName& av_name)
{
+ mAvatarNameCacheConnection.disconnect();
+
std::string label = getName(av_name);
// received the agent name from the server - tell our observers
callObservers(id.asString(), label, mIcon);
@@ -554,9 +562,11 @@ std::string LLUrlEntryAgentName::getLabel(const std::string &url, const LLUrlLab
}
else
{
- LLAvatarNameCache::get(agent_id,
- boost::bind(&LLUrlEntryAgentCompleteName::onAvatarNameCache,
- this, _1, _2));
+ if (mAvatarNameCacheConnection.connected())
+ {
+ mAvatarNameCacheConnection.disconnect();
+ }
+ mAvatarNameCacheConnection = LLAvatarNameCache::get(agent_id, boost::bind(&LLUrlEntryAgentName::onAvatarNameCache, this, _1, _2));
addObserver(agent_id_string, url, cb);
return LLTrans::getString("LoadingData");
}
@@ -597,7 +607,7 @@ LLUrlEntryAgentDisplayName::LLUrlEntryAgentDisplayName()
std::string LLUrlEntryAgentDisplayName::getName(const LLAvatarName& avatar_name)
{
- return avatar_name.mDisplayName;
+ return avatar_name.getDisplayName();
}
//
@@ -613,7 +623,7 @@ LLUrlEntryAgentUserName::LLUrlEntryAgentUserName()
std::string LLUrlEntryAgentUserName::getName(const LLAvatarName& avatar_name)
{
- return avatar_name.mUsername.empty() ? avatar_name.getLegacyName() : avatar_name.mUsername;
+ return avatar_name.getAccountName();
}
//
diff --git a/indra/llui/llurlentry.h b/indra/llui/llurlentry.h
index 5f82721c0f..8c6c32178a 100644
--- a/indra/llui/llurlentry.h
+++ b/indra/llui/llurlentry.h
@@ -171,6 +171,13 @@ class LLUrlEntryAgent : public LLUrlEntryBase
{
public:
LLUrlEntryAgent();
+ ~LLUrlEntryAgent()
+ {
+ if (mAvatarNameCacheConnection.connected())
+ {
+ mAvatarNameCacheConnection.disconnect();
+ }
+ }
/*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;
@@ -181,6 +188,7 @@ 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);
+ boost::signals2::connection mAvatarNameCacheConnection;
};
///
@@ -192,6 +200,13 @@ class LLUrlEntryAgentName : public LLUrlEntryBase, public boost::signals2::track
{
public:
LLUrlEntryAgentName();
+ ~LLUrlEntryAgentName()
+ {
+ if (mAvatarNameCacheConnection.connected())
+ {
+ mAvatarNameCacheConnection.disconnect();
+ }
+ }
/*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
/*virtual*/ LLStyle::Params getStyle() const;
protected:
@@ -199,6 +214,7 @@ protected:
virtual std::string getName(const LLAvatarName& avatar_name) = 0;
private:
void onAvatarNameCache(const LLUUID& id, const LLAvatarName& av_name);
+ boost::signals2::connection mAvatarNameCacheConnection;
};
diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp
index ad9bec9f61..3613a40e2c 100644
--- a/indra/llui/llview.cpp
+++ b/indra/llui/llview.cpp
@@ -55,6 +55,8 @@
#include "lltexteditor.h"
#include "lltextbox.h"
+static const S32 LINE_HEIGHT = 15;
+
S32 LLView::sDepth = 0;
bool LLView::sDebugRects = false;
bool LLView::sDebugRectsShowNames = true;
@@ -349,7 +351,7 @@ void LLView::removeChild(LLView* child)
}
else
{
- llwarns << child->getName() << "is not a child of " << getName() << llendl;
+ llwarns << "\"" << child->getName() << "\" is not a child of " << getName() << llendl;
}
updateBoundingRect();
}
@@ -873,13 +875,12 @@ BOOL LLView::handleToolTip(S32 x, S32 y, MASK mask)
// allow "scrubbing" over ui by showing next tooltip immediately
// if previous one was still visible
F32 timeout = LLToolTipMgr::instance().toolTipVisible()
- ? LLUI::sSettingGroups["config"]->getF32( "ToolTipFastDelay" )
- : LLUI::sSettingGroups["config"]->getF32( "ToolTipDelay" );
+ ? LLUI::sSettingGroups["config"]->getF32( "ToolTipFastDelay" )
+ : LLUI::sSettingGroups["config"]->getF32( "ToolTipDelay" );
LLToolTipMgr::instance().show(LLToolTip::Params()
- .message(tooltip)
- .sticky_rect(calcScreenRect())
- .delay_time(timeout));
-
+ .message(tooltip)
+ .sticky_rect(calcScreenRect())
+ .delay_time(timeout));
handled = TRUE;
}
@@ -1204,11 +1205,24 @@ void LLView::drawDebugRect()
&& preview_iter == sPreviewHighlightedElements.end()
&& sDebugRectsShowNames)
{
- //char temp[256];
S32 x, y;
gGL.color4fv( border_color.mV );
- x = debug_rect.getWidth()/2;
- y = debug_rect.getHeight()/2;
+
+ 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,
diff --git a/indra/llui/llview.h b/indra/llui/llview.h
index 1c35349510..15b85a6418 100644
--- a/indra/llui/llview.h
+++ b/indra/llui/llview.h
@@ -67,7 +67,6 @@ const BOOL NOT_MOUSE_OPAQUE = FALSE;
const U32 GL_NAME_UI_RESERVED = 2;
-
// maintains render state during traversal of UI tree
class LLViewDrawContext
{
diff --git a/indra/llui/llxuiparser.cpp b/indra/llui/llxuiparser.cpp
index afc76024d1..3ad5ad7d42 100644
--- a/indra/llui/llxuiparser.cpp
+++ b/indra/llui/llxuiparser.cpp
@@ -42,7 +42,7 @@
#include <boost/spirit/include/classic_core.hpp>
#include "lluicolor.h"
-
+#include "v3math.h"
using namespace BOOST_SPIRIT_CLASSIC_NS;
const S32 MAX_STRING_ATTRIBUTE_SIZE = 40;
@@ -79,7 +79,6 @@ struct Occurs : public LLInitParam::Block<Occurs>
{}
};
-
typedef enum
{
USE_REQUIRED,
@@ -101,14 +100,23 @@ namespace LLInitParam
struct Element;
struct Group;
-struct Choice;
struct Sequence;
-struct Any;
+
+struct All : public LLInitParam::Block<All, Occurs>
+{
+ Multiple< Lazy<Element, IS_A_BLOCK> > elements;
+
+ All()
+ : elements("element")
+ {
+ maxOccurs = 1;
+ }
+};
struct Attribute : public LLInitParam::Block<Attribute>
{
- Mandatory<std::string> name;
- Mandatory<std::string> type;
+ Mandatory<std::string> name,
+ type;
Mandatory<EUse> use;
Attribute()
@@ -127,24 +135,13 @@ struct Any : public LLInitParam::Block<Any, Occurs>
{}
};
-struct All : public LLInitParam::Block<All, Occurs>
-{
- Multiple< Lazy<Element> > elements;
-
- All()
- : elements("element")
- {
- maxOccurs = 1;
- }
-};
-
struct Choice : public LLInitParam::ChoiceBlock<Choice, Occurs>
{
- Alternative< Lazy<Element> > element;
- Alternative< Lazy<Group> > group;
- Alternative< Lazy<Choice> > choice;
- Alternative< Lazy<Sequence> > sequence;
- Alternative< Lazy<Any> > any;
+ Alternative< Lazy<Element, IS_A_BLOCK> > element;
+ Alternative< Lazy<Group, IS_A_BLOCK> > group;
+ Alternative< Lazy<Choice, IS_A_BLOCK> > choice;
+ Alternative< Lazy<Sequence, IS_A_BLOCK> > sequence;
+ Alternative< Lazy<Any> > any;
Choice()
: element("element"),
@@ -158,11 +155,11 @@ struct Choice : public LLInitParam::ChoiceBlock<Choice, Occurs>
struct Sequence : public LLInitParam::ChoiceBlock<Sequence, Occurs>
{
- Alternative< Lazy<Element> > element;
- Alternative< Lazy<Group> > group;
- Alternative< Lazy<Choice> > choice;
- Alternative< Lazy<Sequence> > sequence;
- Alternative< Lazy<Any> > any;
+ Alternative< Lazy<Element, IS_A_BLOCK> > element;
+ Alternative< Lazy<Group, IS_A_BLOCK> > group;
+ Alternative< Lazy<Choice> > choice;
+ Alternative< Lazy<Sequence, IS_A_BLOCK> > sequence;
+ Alternative< Lazy<Any> > any;
};
struct GroupContents : public LLInitParam::ChoiceBlock<GroupContents, Occurs>
@@ -247,7 +244,7 @@ struct ComplexType : public LLInitParam::Block<ComplexType, ComplexTypeContents>
Optional<bool> mixed;
Multiple<Attribute> attribute;
- Multiple< Lazy<Element> > elements;
+ Multiple< Lazy<Element, IS_A_BLOCK > > elements;
ComplexType()
: name("name"),
@@ -313,7 +310,6 @@ public:
setNameSpace(ns);
};
}
-
};
//
@@ -625,7 +621,7 @@ void LLXUIXSDWriter::writeXSD(const std::string& type_name, const std::string& p
nodep->createChild("schemaLocation", true)->setStringValue(widget_name + ".xsd");
// add to front of schema
- mSchemaNode->addChild(nodep, mSchemaNode);
+ mSchemaNode->addChild(nodep);
}
for (widget_registry_t::Registrar::registry_map_t::const_iterator it = widget_registryp->currentRegistrar().beginItems();
@@ -670,6 +666,7 @@ LLXUIParser::LLXUIParser()
registerParserFuncs<S32>(readS32Value, writeS32Value);
registerParserFuncs<F32>(readF32Value, writeF32Value);
registerParserFuncs<F64>(readF64Value, writeF64Value);
+ registerParserFuncs<LLVector3>(readVector3Value, writeVector3Value);
registerParserFuncs<LLColor4>(readColor4Value, writeColor4Value);
registerParserFuncs<LLUIColor>(readUIColorValue, writeUIColorValue);
registerParserFuncs<LLUUID>(readUUIDValue, writeUUIDValue);
@@ -880,16 +877,24 @@ LLXMLNodePtr LLXUIParser::getNode(name_stack_t& stack)
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)
+ 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();
@@ -1144,6 +1149,31 @@ bool LLXUIParser::writeF64Value(Parser& parser, const void* val_ptr, name_stack_
return false;
}
+bool LLXUIParser::readVector3Value(Parser& parser, void* val_ptr)
+{
+ LLXUIParser& self = static_cast<LLXUIParser&>(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<LLXUIParser&>(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<LLXUIParser&>(parser);
diff --git a/indra/llui/llxuiparser.h b/indra/llui/llxuiparser.h
index d7cd256967..e48663e5cc 100644
--- a/indra/llui/llxuiparser.h
+++ b/indra/llui/llxuiparser.h
@@ -127,6 +127,7 @@ private:
static bool readS32Value(Parser& parser, void* val_ptr);
static bool readF32Value(Parser& parser, void* val_ptr);
static bool readF64Value(Parser& parser, void* val_ptr);
+ static bool readVector3Value(Parser& parser, void* val_ptr);
static bool readColor4Value(Parser& parser, void* val_ptr);
static bool readUIColorValue(Parser& parser, void* val_ptr);
static bool readUUIDValue(Parser& parser, void* val_ptr);
@@ -144,6 +145,7 @@ private:
static bool writeS32Value(Parser& parser, const void* val_ptr, name_stack_t&);
static bool writeF32Value(Parser& parser, const void* val_ptr, name_stack_t&);
static bool writeF64Value(Parser& parser, const void* val_ptr, name_stack_t&);
+ static bool writeVector3Value(Parser& parser, const void* val_ptr, name_stack_t&);
static bool writeColor4Value(Parser& parser, const void* val_ptr, name_stack_t&);
static bool writeUIColorValue(Parser& parser, const void* val_ptr, name_stack_t&);
static bool writeUUIDValue(Parser& parser, const void* val_ptr, name_stack_t&);
diff --git a/indra/llui/tests/llurlentry_stub.cpp b/indra/llui/tests/llurlentry_stub.cpp
index 74ed72ef97..5d3f9ac327 100644
--- a/indra/llui/tests/llurlentry_stub.cpp
+++ b/indra/llui/tests/llurlentry_stub.cpp
@@ -46,11 +46,6 @@ LLAvatarNameCache::callback_connection_t LLAvatarNameCache::get(const LLUUID& ag
return connection;
}
-bool LLAvatarNameCache::useDisplayNames()
-{
- return false;
-}
-
//
// Stub implementation for LLCacheName
//
@@ -106,14 +101,14 @@ LLStyle::Params::Params()
namespace LLInitParam
{
- ParamValue<LLUIColor, TypeValues<LLUIColor> >::ParamValue(const LLUIColor& color)
+ ParamValue<LLUIColor>::ParamValue(const LLUIColor& color)
: super_t(color)
{}
- void ParamValue<LLUIColor, TypeValues<LLUIColor> >::updateValueFromBlock()
+ void ParamValue<LLUIColor>::updateValueFromBlock()
{}
- void ParamValue<LLUIColor, TypeValues<LLUIColor> >::updateBlockFromValue(bool)
+ void ParamValue<LLUIColor>::updateBlockFromValue(bool)
{}
bool ParamCompare<const LLFontGL*, false>::equals(const LLFontGL* a, const LLFontGL* b)
@@ -121,14 +116,14 @@ namespace LLInitParam
return false;
}
- ParamValue<const LLFontGL*, TypeValues<const LLFontGL*> >::ParamValue(const LLFontGL* fontp)
+ ParamValue<const LLFontGL*>::ParamValue(const LLFontGL* fontp)
: super_t(fontp)
{}
- void ParamValue<const LLFontGL*, TypeValues<const LLFontGL*> >::updateValueFromBlock()
+ void ParamValue<const LLFontGL*>::updateValueFromBlock()
{}
- void ParamValue<const LLFontGL*, TypeValues<const LLFontGL*> >::updateBlockFromValue(bool)
+ void ParamValue<const LLFontGL*>::updateBlockFromValue(bool)
{}
void TypeValues<LLFontGL::HAlign>::declareValues()
@@ -140,10 +135,10 @@ namespace LLInitParam
void TypeValues<LLFontGL::ShadowType>::declareValues()
{}
- void ParamValue<LLUIImage*, TypeValues<LLUIImage*> >::updateValueFromBlock()
+ void ParamValue<LLUIImage*>::updateValueFromBlock()
{}
- void ParamValue<LLUIImage*, TypeValues<LLUIImage*> >::updateBlockFromValue(bool)
+ void ParamValue<LLUIImage*>::updateBlockFromValue(bool)
{}
diff --git a/indra/llui/tests/llurlmatch_test.cpp b/indra/llui/tests/llurlmatch_test.cpp
index 963473c92a..109d3ca7bb 100644
--- a/indra/llui/tests/llurlmatch_test.cpp
+++ b/indra/llui/tests/llurlmatch_test.cpp
@@ -63,14 +63,14 @@ S32 LLUIImage::getHeight() const
namespace LLInitParam
{
- ParamValue<LLUIColor, TypeValues<LLUIColor> >::ParamValue(const LLUIColor& color)
+ ParamValue<LLUIColor>::ParamValue(const LLUIColor& color)
: super_t(color)
{}
- void ParamValue<LLUIColor, TypeValues<LLUIColor> >::updateValueFromBlock()
+ void ParamValue<LLUIColor>::updateValueFromBlock()
{}
- void ParamValue<LLUIColor, TypeValues<LLUIColor> >::updateBlockFromValue(bool)
+ void ParamValue<LLUIColor>::updateBlockFromValue(bool)
{}
bool ParamCompare<const LLFontGL*, false>::equals(const LLFontGL* a, const LLFontGL* b)
@@ -79,14 +79,14 @@ namespace LLInitParam
}
- ParamValue<const LLFontGL*, TypeValues<const LLFontGL*> >::ParamValue(const LLFontGL* fontp)
+ ParamValue<const LLFontGL*>::ParamValue(const LLFontGL* fontp)
: super_t(fontp)
{}
- void ParamValue<const LLFontGL*, TypeValues<const LLFontGL*> >::updateValueFromBlock()
+ void ParamValue<const LLFontGL*>::updateValueFromBlock()
{}
- void ParamValue<const LLFontGL*, TypeValues<const LLFontGL*> >::updateBlockFromValue(bool)
+ void ParamValue<const LLFontGL*>::updateBlockFromValue(bool)
{}
void TypeValues<LLFontGL::HAlign>::declareValues()
@@ -98,10 +98,10 @@ namespace LLInitParam
void TypeValues<LLFontGL::ShadowType>::declareValues()
{}
- void ParamValue<LLUIImage*, TypeValues<LLUIImage*> >::updateValueFromBlock()
+ void ParamValue<LLUIImage*>::updateValueFromBlock()
{}
- void ParamValue<LLUIImage*, TypeValues<LLUIImage*> >::updateBlockFromValue(bool)
+ void ParamValue<LLUIImage*>::updateBlockFromValue(bool)
{}
bool ParamCompare<LLUIImage*, false>::equals(