summaryrefslogtreecommitdiff
path: root/indra/llui
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llui')
-rw-r--r--indra/llui/CMakeLists.txt2
-rw-r--r--indra/llui/lldockablefloater.cpp14
-rw-r--r--indra/llui/lldockablefloater.h2
-rw-r--r--indra/llui/lldockcontrol.cpp23
-rw-r--r--indra/llui/lldockcontrol.h4
-rw-r--r--indra/llui/llfiltereditor.cpp74
-rw-r--r--indra/llui/llfiltereditor.h30
-rw-r--r--indra/llui/llflatlistview.cpp494
-rw-r--r--indra/llui/llflatlistview.h262
-rw-r--r--indra/llui/llfloater.cpp11
-rw-r--r--indra/llui/lllineeditor.h2
-rw-r--r--indra/llui/llsearcheditor.cpp82
-rw-r--r--indra/llui/llsearcheditor.h24
-rw-r--r--indra/llui/lltabcontainer.cpp11
-rw-r--r--indra/llui/llurlentry.cpp81
-rw-r--r--indra/llui/llurlentry.h28
-rw-r--r--indra/llui/llurlregistry.cpp7
-rw-r--r--indra/llui/llview.h2
-rw-r--r--indra/llui/tests/llurlentry_test.cpp72
19 files changed, 996 insertions, 229 deletions
diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt
index cc9362a252..fce6b759a4 100644
--- a/indra/llui/CMakeLists.txt
+++ b/indra/llui/CMakeLists.txt
@@ -40,6 +40,7 @@ set(llui_SOURCE_FILES
lleditmenuhandler.cpp
llf32uictrl.cpp
llfiltereditor.cpp
+ llflatlistview.cpp
llfloater.cpp
llfloaterreg.cpp
llflyoutbutton.cpp
@@ -122,6 +123,7 @@ set(llui_HEADER_FILES
lleditmenuhandler.h
llf32uictrl.h
llfiltereditor.h
+ llflatlistview.h
llfloater.h
llfloaterreg.h
llflyoutbutton.h
diff --git a/indra/llui/lldockablefloater.cpp b/indra/llui/lldockablefloater.cpp
index 29f78f6290..ed15d9d922 100644
--- a/indra/llui/lldockablefloater.cpp
+++ b/indra/llui/lldockablefloater.cpp
@@ -35,7 +35,7 @@
#include "lldockablefloater.h"
//static
-LLDockableFloater* LLDockableFloater::instance = NULL;
+LLHandle<LLFloater> LLDockableFloater::instanceHandle;
LLDockableFloater::LLDockableFloater(LLDockControl* dockControl,
const LLSD& key, const Params& params) :
@@ -57,21 +57,21 @@ BOOL LLDockableFloater::postBuild()
void LLDockableFloater::resetInstance()
{
- if (instance != this)
+ if (instanceHandle.get() != this)
{
- if (instance != NULL && instance->isDocked())
+ if (instanceHandle.get() != NULL && instanceHandle.get()->isDocked())
{
//closeFloater() is not virtual
- if (instance->canClose())
+ if (instanceHandle.get()->canClose())
{
- instance->closeFloater();
+ instanceHandle.get()->closeFloater();
}
else
{
- instance->setVisible(FALSE);
+ instanceHandle.get()->setVisible(FALSE);
}
}
- instance = this;
+ instanceHandle = getHandle();
}
}
diff --git a/indra/llui/lldockablefloater.h b/indra/llui/lldockablefloater.h
index b977888803..1d0e89cef5 100644
--- a/indra/llui/lldockablefloater.h
+++ b/indra/llui/lldockablefloater.h
@@ -69,7 +69,7 @@ protected:
private:
std::auto_ptr<LLDockControl> mDockControl;
LLUIImagePtr mDockTongue;
- static LLDockableFloater* instance;
+ static LLHandle<LLFloater> instanceHandle;
};
#endif /* LL_DOCKABLEFLOATER_H */
diff --git a/indra/llui/lldockcontrol.cpp b/indra/llui/lldockcontrol.cpp
index bec7f04cc0..d666f2be56 100644
--- a/indra/llui/lldockcontrol.cpp
+++ b/indra/llui/lldockcontrol.cpp
@@ -48,12 +48,25 @@ LLDockControl::LLDockControl(LLView* dockWidget, LLFloater* dockableFloater,
{
off();
}
+
+ if (dockWidget != NULL) {
+ repositionDockable();
+ }
}
LLDockControl::~LLDockControl()
{
}
+void LLDockControl::setDock(LLView* dockWidget)
+{
+ mDockWidget = dockWidget;
+ if (mDockWidget != NULL)
+ {
+ repositionDockable();
+ }
+}
+
void LLDockControl::repositionDockable()
{
if (mEnabled)
@@ -65,11 +78,14 @@ void LLDockControl::repositionDockable()
void LLDockControl::calculateDockablePosition()
{
LLRect dockRect = mDockWidget->calcScreenRect();
- if (mPrevDockRect != dockRect || mRecalculateDocablePosition)
+ LLRect rootRect = mDockableFloater->getRootView()->getRect();
+
+ // recalculate dockable position if dock position changed
+ // or root view rect changed or recalculation is forced
+ if (mPrevDockRect != dockRect || mRootRect != rootRect
+ || mRecalculateDocablePosition)
{
LLRect dockableRect = mDockableFloater->calcScreenRect();
- LLRect rootRect = mDockableFloater->getRootView()->getRect();
-
S32 x = 0;
S32 y = 0;
switch (mDockAt)
@@ -100,6 +116,7 @@ void LLDockControl::calculateDockablePosition()
mDockableFloater->screenPointToLocal(mDockTongueX, mDockTongueY,
&mDockTongueX, &mDockTongueY);
mPrevDockRect = dockRect;
+ mRootRect = rootRect;
mRecalculateDocablePosition = false;
}
}
diff --git a/indra/llui/lldockcontrol.h b/indra/llui/lldockcontrol.h
index 0e1f4c8e64..7d8d5c7653 100644
--- a/indra/llui/lldockcontrol.h
+++ b/indra/llui/lldockcontrol.h
@@ -60,8 +60,7 @@ public:
public:
void on();
void off();
- void setDock(LLView* dockWidget)
- { mDockWidget = dockWidget;};
+ void setDock(LLView* dockWidget);
void repositionDockable();
void drawToungue();
protected:
@@ -72,6 +71,7 @@ private:
DocAt mDockAt;
LLView* mDockWidget;
LLRect mPrevDockRect;
+ LLRect mRootRect;
LLFloater* mDockableFloater;
LLUIImagePtr mDockTongue;
S32 mDockTongueX;
diff --git a/indra/llui/llfiltereditor.cpp b/indra/llui/llfiltereditor.cpp
index 26b5f2e182..390504234d 100644
--- a/indra/llui/llfiltereditor.cpp
+++ b/indra/llui/llfiltereditor.cpp
@@ -37,81 +37,15 @@
#include "llfiltereditor.h"
LLFilterEditor::LLFilterEditor(const LLFilterEditor::Params& p)
-: LLUICtrl(p)
+: LLSearchEditor(p)
{
- LLLineEditor::Params line_editor_p(p);
- line_editor_p.name("filter edit box");
- line_editor_p.rect(getLocalRect());
- line_editor_p.follows.flags(FOLLOWS_ALL);
- line_editor_p.text_pad_right(getRect().getHeight());
- line_editor_p.revert_on_esc(false);
- line_editor_p.keystroke_callback(boost::bind(&LLUICtrl::onCommit, this));
-
- mFilterEditor = LLUICtrlFactory::create<LLLineEditor>(line_editor_p);
- addChild(mFilterEditor);
-
- S32 btn_width = getRect().getHeight(); // button is square, and as tall as search editor
- LLRect clear_btn_rect(getRect().getWidth() - btn_width, getRect().getHeight(), getRect().getWidth(), 0);
- LLButton::Params button_params(p.clear_filter_button);
- button_params.name(std::string("clear filter"));
- button_params.rect(clear_btn_rect) ;
- button_params.follows.flags(FOLLOWS_RIGHT|FOLLOWS_TOP);
- button_params.tab_stop(false);
- button_params.click_callback.function(boost::bind(&LLFilterEditor::onClearFilter, this, _2));
-
- mClearFilterButton = LLUICtrlFactory::create<LLButton>(button_params);
- mFilterEditor->addChild(mClearFilterButton);
-}
-
-//virtual
-void LLFilterEditor::setValue(const LLSD& value )
-{
- mFilterEditor->setValue(value);
-}
-
-//virtual
-LLSD LLFilterEditor::getValue() const
-{
- return mFilterEditor->getValue();
-}
-
-//virtual
-BOOL LLFilterEditor::setTextArg( const std::string& key, const LLStringExplicit& text )
-{
- return mFilterEditor->setTextArg(key, text);
}
-//virtual
-BOOL LLFilterEditor::setLabelArg( const std::string& key, const LLStringExplicit& text )
-{
- return mFilterEditor->setLabelArg(key, text);
-}
-
-//virtual
-void LLFilterEditor::setLabel( const LLStringExplicit &new_label )
-{
- mFilterEditor->setLabel(new_label);
-}
-
-//virtual
-void LLFilterEditor::clear()
-{
- if (mFilterEditor)
- {
- mFilterEditor->clear();
- }
-}
-void LLFilterEditor::draw()
+void LLFilterEditor::handleKeystroke()
{
- mClearFilterButton->setVisible(!mFilterEditor->getWText().empty());
-
- LLUICtrl::draw();
-}
+ this->LLSearchEditor::handleKeystroke();
-void LLFilterEditor::onClearFilter(const LLSD& data)
-{
- setText(LLStringUtil::null);
+ // Commit on every keystroke.
onCommit();
}
-
diff --git a/indra/llui/llfiltereditor.h b/indra/llui/llfiltereditor.h
index fceb82af8d..c43a76b130 100644
--- a/indra/llui/llfiltereditor.h
+++ b/indra/llui/llfiltereditor.h
@@ -42,18 +42,14 @@
#ifndef LL_FILTEREDITOR_H
#define LL_FILTEREDITOR_H
-#include "lllineeditor.h"
-#include "llbutton.h"
+#include "llsearcheditor.h"
-class LLFilterEditor : public LLUICtrl
+class LLFilterEditor : public LLSearchEditor
{
public:
- struct Params : public LLInitParam::Block<Params, LLLineEditor::Params>
+ struct Params : public LLInitParam::Block<Params, LLSearchEditor::Params>
{
- Optional<LLButton::Params> clear_filter_button;
-
Params()
- : clear_filter_button("clear_filter_button")
{
name = "filter_editor";
}
@@ -62,26 +58,8 @@ public:
protected:
LLFilterEditor(const Params&);
friend class LLUICtrlFactory;
-public:
- virtual ~LLFilterEditor() {}
-
- /*virtual*/ void draw();
-
- void setText(const LLStringExplicit &new_text) { mFilterEditor->setText(new_text); }
-
- // LLUICtrl interface
- virtual void setValue(const LLSD& value );
- virtual LLSD getValue() const;
- virtual BOOL setTextArg( const std::string& key, const LLStringExplicit& text );
- virtual BOOL setLabelArg( const std::string& key, const LLStringExplicit& text );
- virtual void setLabel( const LLStringExplicit &new_label );
- virtual void clear();
-
-private:
- void onClearFilter(const LLSD& data);
- LLLineEditor* mFilterEditor;
- LLButton* mClearFilterButton;
+ /*virtual*/ void handleKeystroke();
};
#endif // LL_FILTEREDITOR_H
diff --git a/indra/llui/llflatlistview.cpp b/indra/llui/llflatlistview.cpp
new file mode 100644
index 0000000000..75334acb39
--- /dev/null
+++ b/indra/llui/llflatlistview.cpp
@@ -0,0 +1,494 @@
+/**
+ * @file llflatlistview.cpp
+ * @brief LLFlatListView base class
+ *
+ * $LicenseInfo:firstyear=2009&license=viewergpl$
+ *
+ * Copyright (c) 2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llpanel.h"
+
+#include "llflatlistview.h"
+
+static const LLDefaultChildRegistry::Register<LLFlatListView> flat_list_view("flat_list_view");
+
+const LLSD SELECTED_EVENT = LLSD().insert("selected", true);
+const LLSD UNSELECTED_EVENT = LLSD().insert("selected", false);
+
+LLFlatListView::Params::Params()
+: item_pad("item_pad"),
+ allow_select("allow_select"),
+ multi_select("multi_select"),
+ keep_one_selected("keep_one_selected")
+{};
+
+void LLFlatListView::reshape(S32 width, S32 height, BOOL called_from_parent /* = TRUE */)
+{
+ LLScrollContainer::reshape(width, height, called_from_parent);
+ setItemsNoScrollWidth(width);
+ rearrangeItems();
+}
+
+bool LLFlatListView::addItem(LLPanel* item, LLSD value /* = LLUUID::null*/, EAddPosition pos /*= ADD_BOTTOM*/)
+{
+ if (!item) return false;
+ if (value.isUndefined()) return false;
+
+ //force uniqueness of items, easiest check but unreliable
+ if (item->getParent() == mItemsPanel) return false;
+
+ item_pair_t* new_pair = new item_pair_t(item, value);
+ switch (pos)
+ {
+ case ADD_TOP:
+ mItemPairs.push_front(new_pair);
+ //in LLView::draw() children are iterated in backorder
+ mItemsPanel->addChildInBack(item);
+ break;
+ case ADD_BOTTOM:
+ mItemPairs.push_back(new_pair);
+ mItemsPanel->addChild(item);
+ break;
+ default:
+ break;
+ }
+
+ //_4 is for MASK
+ item->setMouseDownCallback(boost::bind(&LLFlatListView::onItemMouseClick, this, new_pair, _4));
+ item->setRightMouseDownCallback(boost::bind(&LLFlatListView::onItemMouseClick, this, new_pair, _4));
+
+ rearrangeItems();
+ return true;
+}
+
+
+bool LLFlatListView::insertItemAfter(LLPanel* after_item, LLPanel* item_to_add, LLSD value /*= LLUUID::null*/)
+{
+ if (!after_item) return false;
+ if (!item_to_add) return false;
+ if (value.isUndefined()) return false;
+
+ if (mItemPairs.empty()) return false;
+
+ //force uniqueness of items, easiest check but unreliable
+ if (item_to_add->getParent() == mItemsPanel) return false;
+
+ item_pair_t* after_pair = getItemPair(after_item);
+ if (!after_pair) return false;
+
+ item_pair_t* new_pair = new item_pair_t(item_to_add, value);
+ if (after_pair == mItemPairs.back())
+ {
+ mItemPairs.push_back(new_pair);
+ mItemsPanel->addChild(item_to_add);
+ }
+ else
+ {
+ pairs_iterator_t it = mItemPairs.begin();
+ ++it;
+ while (it != mItemPairs.end())
+ {
+ if (*it == after_pair)
+ {
+ mItemPairs.insert(++it, new_pair);
+ mItemsPanel->addChild(item_to_add);
+ break;
+ }
+ }
+ }
+
+ //_4 is for MASK
+ item_to_add->setMouseDownCallback(boost::bind(&LLFlatListView::onItemMouseClick, this, new_pair, _4));
+ item_to_add->setRightMouseDownCallback(boost::bind(&LLFlatListView::onItemMouseClick, this, new_pair, _4));
+
+ rearrangeItems();
+ return true;
+}
+
+
+bool LLFlatListView::removeItem(LLPanel* item)
+{
+ if (!item) return false;
+ if (item->getParent() != mItemsPanel) return false;
+
+ item_pair_t* item_pair = getItemPair(item);
+ if (!item_pair) return false;
+
+ return removeItemPair(item_pair);
+}
+
+bool LLFlatListView::removeItemByValue(const LLSD& value)
+{
+ if (value.isUndefined()) return false;
+
+ item_pair_t* item_pair = getItemPair(value);
+ if (!item_pair) return false;
+
+ return removeItemPair(item_pair);
+}
+
+bool LLFlatListView::removeItemByUUID(LLUUID& uuid)
+{
+ return removeItemByValue(LLSD(uuid));
+}
+
+LLPanel* LLFlatListView::getItemByValue(LLSD& value) const
+{
+ if (value.isDefined()) return NULL;
+
+ item_pair_t* pair = getItemPair(value);
+ if (pair) return pair->first;
+ return NULL;
+}
+
+bool LLFlatListView::selectItem(LLPanel* item, bool select /*= true*/)
+{
+ if (!item) return false;
+ if (item->getParent() != mItemsPanel) return false;
+
+ item_pair_t* item_pair = getItemPair(item);
+ if (!item_pair) return false;
+
+ return selectItemPair(item_pair, select);
+}
+
+bool LLFlatListView::selectItemByValue(const LLSD& value, bool select /*= true*/)
+{
+ if (value.isUndefined()) return false;
+
+ item_pair_t* item_pair = getItemPair(value);
+ if (!item_pair) return false;
+
+ return selectItemPair(item_pair, select);
+}
+
+bool LLFlatListView::selectItemByUUID(LLUUID& uuid, bool select /* = true*/)
+{
+ return selectItemByValue(LLSD(uuid), select);
+}
+
+
+LLSD LLFlatListView::getSelectedValue() const
+{
+ if (mSelectedItemPairs.empty()) return LLSD();
+
+ item_pair_t* first_selected_pair = mSelectedItemPairs.front();
+ return first_selected_pair->second;
+}
+
+void LLFlatListView::getSelectedValues(std::vector<LLSD>& selected_values) const
+{
+ if (mSelectedItemPairs.empty()) return;
+
+ for (pairs_const_iterator_t it = mSelectedItemPairs.begin(); it != mSelectedItemPairs.end(); ++it)
+ {
+ selected_values.push_back((*it)->second);
+ }
+}
+
+LLUUID LLFlatListView::getSelectedUUID() const
+{
+ const LLSD& value = getSelectedValue();
+ if (value.isDefined() && value.isUUID())
+ {
+ return value.asUUID();
+ }
+ else
+ {
+ return LLUUID::null;
+ }
+}
+
+void LLFlatListView::getSelectedUUIDs(std::vector<LLUUID>& selected_uuids) const
+{
+ if (mSelectedItemPairs.empty()) return;
+
+ for (pairs_const_iterator_t it = mSelectedItemPairs.begin(); it != mSelectedItemPairs.end(); ++it)
+ {
+ selected_uuids.push_back((*it)->second.asUUID());
+ }
+}
+
+LLPanel* LLFlatListView::getSelectedItem() const
+{
+ if (mSelectedItemPairs.empty()) return NULL;
+
+ return mSelectedItemPairs.front()->first;
+}
+
+void LLFlatListView::getSelectedItems(std::vector<LLPanel*>& selected_items) const
+{
+ if (mSelectedItemPairs.empty()) return;
+
+ for (pairs_const_iterator_t it = mSelectedItemPairs.begin(); it != mSelectedItemPairs.end(); ++it)
+ {
+ selected_items.push_back((*it)->first);
+ }
+}
+
+void LLFlatListView::resetSelection()
+{
+ if (mSelectedItemPairs.empty()) return;
+
+ for (pairs_iterator_t it= mSelectedItemPairs.begin(); it != mSelectedItemPairs.end(); ++it)
+ {
+ item_pair_t* pair_to_deselect = *it;
+ LLPanel* item = pair_to_deselect->first;
+ item->setValue(UNSELECTED_EVENT);
+ }
+
+ mSelectedItemPairs.clear();
+}
+
+void LLFlatListView::clear()
+{
+ // do not use LLView::deleteAllChildren to avoid removing nonvisible items. drag-n-drop for ex.
+ for (pairs_iterator_t it = mItemPairs.begin(); it != mItemPairs.end(); ++it)
+ {
+ mItemsPanel->removeChild((*it)->first);
+ delete (*it)->first;
+ delete *it;
+ }
+ mItemPairs.clear();
+ mSelectedItemPairs.clear();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// PROTECTED STUFF
+//////////////////////////////////////////////////////////////////////////
+
+
+LLFlatListView::LLFlatListView(const LLFlatListView::Params& p)
+: LLScrollContainer(p),
+ mItemsPanel(NULL),
+ mItemPad(p.item_pad),
+ mAllowSelection(p.allow_select),
+ mMultipleSelection(p.multi_select),
+ mKeepOneItemSelected(p.keep_one_selected)
+{
+ mBorderThickness = getBorderWidth();
+
+ LLRect scroll_rect = getRect();
+ LLRect items_rect;
+
+ setItemsNoScrollWidth(scroll_rect.getWidth());
+ items_rect.setLeftTopAndSize(mBorderThickness, scroll_rect.getHeight() - mBorderThickness, mItemsNoScrollWidth, 0);
+
+ LLPanel::Params pp;
+ pp.rect(items_rect);
+ mItemsPanel = LLUICtrlFactory::create<LLPanel> (pp);
+ addChild(mItemsPanel);
+
+ //we don't need to stretch in vertical direction on reshaping by a parent
+ //no bottom following!
+ mItemsPanel->setFollows(FOLLOWS_LEFT | FOLLOWS_RIGHT | FOLLOWS_TOP);
+};
+
+void LLFlatListView::rearrangeItems()
+{
+ static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
+
+ if (mItemPairs.empty()) return;
+
+ //calculating required height - assuming items can be of different height
+ //list should accommodate all its items
+ S32 height = 0;
+
+ pairs_iterator_t it = mItemPairs.begin();
+ for (; it != mItemPairs.end(); ++it)
+ {
+ LLPanel* item = (*it)->first;
+ height += item->getRect().getHeight();
+ }
+ height += mItemPad * (mItemPairs.size() - 1);
+
+ LLRect rc = mItemsPanel->getRect();
+ S32 width = mItemsNoScrollWidth;
+
+ // update width to avoid horizontal scrollbar
+ if (height > getRect().getHeight() - 2 * mBorderThickness)
+ width -= scrollbar_size;
+
+ //changes the bottom, end of the list goes down in the scroll container
+ rc.setLeftTopAndSize(rc.mLeft, rc.mTop, width, height);
+ mItemsPanel->setRect(rc);
+
+ //reshaping items
+ S32 item_new_top = height;
+ pairs_iterator_t it2, first_it = mItemPairs.begin();
+ for (it2 = first_it; it2 != mItemPairs.end(); ++it2)
+ {
+ LLPanel* item = (*it2)->first;
+ LLRect rc = item->getRect();
+ if(it2 != first_it)
+ {
+ item_new_top -= (rc.getHeight() + mItemPad);
+ }
+ rc.setLeftTopAndSize(rc.mLeft, item_new_top, width, rc.getHeight());
+ item->reshape(rc.getWidth(), rc.getHeight());
+ item->setRect(rc);
+ }
+}
+
+void LLFlatListView::onItemMouseClick(item_pair_t* item_pair, MASK mask)
+{
+ if (!item_pair) return;
+
+ bool select_item = !isSelected(item_pair);
+
+ //*TODO find a better place for that enforcing stuff
+ if (mKeepOneItemSelected && numSelected() == 1 && !select_item) return;
+
+ if (!(mask & MASK_CONTROL) || !mMultipleSelection) resetSelection();
+ selectItemPair(item_pair, select_item);
+}
+
+LLFlatListView::item_pair_t* LLFlatListView::getItemPair(LLPanel* item) const
+{
+ llassert(item);
+
+ for (pairs_const_iterator_t it= mItemPairs.begin(); it != mItemPairs.end(); ++it)
+ {
+ item_pair_t* item_pair = *it;
+ if (item_pair->first == item) return item_pair;
+ }
+ return NULL;
+}
+
+//compares two LLSD's
+bool llsds_are_equal(const LLSD& llsd_1, const LLSD& llsd_2)
+{
+ llassert(llsd_1.isDefined());
+ llassert(llsd_2.isDefined());
+
+ if (llsd_1.type() != llsd_2.type()) return false;
+
+ if (!llsd_1.isMap())
+ {
+ if (llsd_1.isUUID()) return llsd_1.asUUID() == llsd_2.asUUID();
+
+ //assumptions that string representaion is enough for other types
+ return llsd_1.asString() == llsd_2.asString();
+ }
+
+ if (llsd_1.size() != llsd_2.size()) return false;
+
+ LLSD::map_const_iterator llsd_1_it = llsd_1.beginMap();
+ LLSD::map_const_iterator llsd_2_it = llsd_2.beginMap();
+ for (S32 i = 0; i < llsd_1.size(); ++i)
+ {
+ if ((*llsd_1_it).first != (*llsd_2_it).first) return false;
+ if (!llsds_are_equal((*llsd_1_it).second, (*llsd_2_it).second)) return false;
+ ++llsd_1_it;
+ ++llsd_2_it;
+ }
+ return true;
+}
+
+LLFlatListView::item_pair_t* LLFlatListView::getItemPair(const LLSD& value) const
+{
+ llassert(value.isDefined());
+
+ for (pairs_const_iterator_t it= mItemPairs.begin(); it != mItemPairs.end(); ++it)
+ {
+ item_pair_t* item_pair = *it;
+ if (llsds_are_equal(item_pair->second, value)) return item_pair;
+ }
+ return NULL;
+}
+
+bool LLFlatListView::selectItemPair(item_pair_t* item_pair, bool select)
+{
+ llassert(item_pair);
+
+ if (!mAllowSelection && select) return false;
+
+ if (isSelected(item_pair) == select) return true; //already in specified selection state
+ if (select)
+ {
+ mSelectedItemPairs.push_back(item_pair);
+ }
+ else
+ {
+ mSelectedItemPairs.remove(item_pair);
+ }
+
+ //a way of notifying panel of selection state changes
+ LLPanel* item = item_pair->first;
+ item->setValue(select ? SELECTED_EVENT : UNSELECTED_EVENT);
+ return true;
+}
+
+bool LLFlatListView::isSelected(item_pair_t* item_pair) const
+{
+ llassert(item_pair);
+
+ pairs_const_iterator_t it_end = mSelectedItemPairs.end();
+ return std::find(mSelectedItemPairs.begin(), it_end, item_pair) != it_end;
+}
+
+bool LLFlatListView::removeItemPair(item_pair_t* item_pair)
+{
+ llassert(item_pair);
+
+ bool deleted = false;
+ for (pairs_iterator_t it = mItemPairs.begin(); it != mItemPairs.end(); ++it)
+ {
+ item_pair_t* _item_pair = *it;
+ if (_item_pair == item_pair)
+ {
+ mItemPairs.erase(it);
+ deleted = true;
+ break;
+ }
+ }
+
+ if (!deleted) return false;
+
+ for (pairs_iterator_t it = mSelectedItemPairs.begin(); it != mSelectedItemPairs.end(); ++it)
+ {
+ item_pair_t* selected_item_pair = *it;
+ if (selected_item_pair == item_pair)
+ {
+ it = mSelectedItemPairs.erase(it);
+ break;
+ }
+ }
+
+ mItemsPanel->removeChild(item_pair->first);
+ delete item_pair->first;
+ delete item_pair;
+
+ rearrangeItems();
+
+ return true;
+}
+
+
diff --git a/indra/llui/llflatlistview.h b/indra/llui/llflatlistview.h
new file mode 100644
index 0000000000..bd0b419f4f
--- /dev/null
+++ b/indra/llui/llflatlistview.h
@@ -0,0 +1,262 @@
+/**
+ * @file llflatlistview.h
+ * @brief LLFlatListView base class
+ *
+ * $LicenseInfo:firstyear=2009&license=viewergpl$
+ *
+ * Copyright (c) 2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLFLATLISTVIEW_H
+#define LL_LLFLATLISTVIEW_H
+
+#include "llscrollcontainer.h"
+
+
+class LLPanel;
+
+/**
+ * LLFlatListView represents a flat list ui control that operates on items in a form of LLPanel's.
+ * LLSD can be associated with each added item, it can keep data from an item in digested form.
+ * Associated LLSD's can be of any type (singular, a map etc.).
+ * Items (LLPanel's subclasses) can be of different height.
+ * The list is LLPanel created in itself and grows in height while new items are added.
+ *
+ * The control can manage selection of its items when the flag "allow_select" is set. Also ability to select
+ * multiple items (by using CTRL) is enabled through setting the flag "multi_select" - if selection is not allowed that flag
+ * is ignored. The option "keep_one_selected" forces at least one item to be selected at any time (only for mouse events on items)
+ * since any item of the list was selected.
+ *
+ * Examples of using this control are presented in Picks panel (Me Profile and Profile View), where this control is used to
+ * manage the list of pick items.
+ *
+ * ASSUMPTIONS AND STUFF
+ * - NULL pointers and undefined LLSD's are not accepted by any method of this class unless specified otherwise
+ * - Order of returned selected items are not guaranteed
+ * - The control assumes that all items being added are unique.
+ */
+class LLFlatListView : public LLScrollContainer
+{
+public:
+
+ struct Params : public LLInitParam::Block<Params, LLScrollContainer::Params>
+ {
+ /** turning on/off selection support */
+ Optional<bool> allow_select;
+
+ /** turning on/off multiple selection (works while clicking and holding CTRL)*/
+ Optional<bool> multi_select;
+
+ /** don't allow to deselect all selected items (for mouse events on items only) */
+ Optional<bool> keep_one_selected;
+
+ /** padding between items */
+ Optional<U32> item_pad;
+
+ Params();
+ };
+
+ virtual ~LLFlatListView() { clear(); };
+
+
+ /** Overridden LLPanel's reshape, height is ignored, the list sets its height to accommodate all items */
+ virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
+
+
+ /**
+ * Adds and item and LLSD value associated with it to the list at specified position
+ * @return true if the item was added, false otherwise
+ */
+ virtual bool addItem(LLPanel* item, LLSD value = LLUUID::null, EAddPosition pos = ADD_BOTTOM);
+
+ /**
+ * Insert item_to_add along with associated value to the list right after the after_item.
+ * @return true if the item was successfully added, false otherwise
+ */
+ virtual bool insertItemAfter(LLPanel* after_item, LLPanel* item_to_add, LLSD value = LLUUID::null);
+
+ /**
+ * Remove specified item
+ * @return true if the item was removed, false otherwise
+ */
+ virtual bool removeItem(LLPanel* item);
+
+ /**
+ * Remove an item specified by value
+ * @return true if the item was removed, false otherwise
+ */
+ virtual bool removeItemByValue(const LLSD& value);
+
+ /**
+ * Remove an item specified by uuid
+ * @return true if the item was removed, false otherwise
+ */
+ virtual bool removeItemByUUID(LLUUID& uuid);
+
+ /**
+ * Get an item by value
+ * @return the item as LLPanel if associated with value, NULL otherwise
+ */
+ virtual LLPanel* getItemByValue(LLSD& value) const;
+
+ /**
+ * Select or deselect specified item based on select
+ * @return true if succeed, false otherwise
+ */
+ virtual bool selectItem(LLPanel* item, bool select = true);
+
+ /**
+ * Select or deselect an item by associated value based on select
+ * @return true if succeed, false otherwise
+ */
+ virtual bool selectItemByValue(const LLSD& value, bool select = true);
+
+ /**
+ * Select or deselect an item by associated uuid based on select
+ * @return true if succeed, false otherwise
+ */
+ virtual bool selectItemByUUID(LLUUID& uuid, bool select = true);
+
+
+
+ /**
+ * Get LLSD associated with the first selected item
+ */
+ virtual LLSD getSelectedValue() const;
+
+ /**
+ * Get LLSD's associated with selected items.
+ * @param selected_values std::vector being populated with LLSD associated with selected items
+ */
+ virtual void getSelectedValues(std::vector<LLSD>& selected_values) const;
+
+
+ /**
+ * Get LLUUID associated with selected item
+ * @return LLUUID if such was associated with selected item
+ */
+ virtual LLUUID getSelectedUUID() const;
+
+ /**
+ * Get LLUUIDs associated with selected items
+ * @param selected_uuids An std::vector being populated with LLUUIDs associated with selected items
+ */
+ virtual void getSelectedUUIDs(std::vector<LLUUID>& selected_uuids) const;
+
+ /** Get the top selected item */
+ virtual LLPanel* getSelectedItem() const;
+
+ /**
+ * Get selected items
+ * @param selected_items An std::vector being populated with pointers to selected items
+ */
+ virtual void getSelectedItems(std::vector<LLPanel*>& selected_items) const;
+
+
+ /** Resets selection of items */
+ virtual void resetSelection();
+
+
+ /** Turn on/off multiple selection support */
+ void setAllowMultipleSelection(bool allow) { mMultipleSelection = allow; }
+
+ /** Turn on/off selection support */
+ void setAllowSelection(bool can_select) { mAllowSelection = can_select; }
+
+
+ /** Get number of selected items in the list */
+ U32 numSelected() const {return mSelectedItemPairs.size(); }
+
+ /** Get number of items in the list */
+ U32 size() const { return mItemPairs.size(); }
+
+
+ /** Removes all items from the list */
+ virtual void clear();
+
+
+protected:
+
+ /** Pairs LLpanel representing a single item LLPanel and LLSD associated with it */
+ typedef std::pair<LLPanel*, LLSD> item_pair_t;
+
+ typedef std::list<item_pair_t*> pairs_list_t;
+ typedef pairs_list_t::iterator pairs_iterator_t;
+ typedef pairs_list_t::const_iterator pairs_const_iterator_t;
+
+
+ friend class LLUICtrlFactory;
+ LLFlatListView(const LLFlatListView::Params& p);
+
+ /** Manage selection on mouse events */
+ void onItemMouseClick(item_pair_t* item_pair, MASK mask);
+
+ /** Updates position of items */
+ virtual void rearrangeItems();
+
+ virtual item_pair_t* getItemPair(LLPanel* item) const;
+
+ virtual item_pair_t* getItemPair(const LLSD& value) const;
+
+ virtual bool selectItemPair(item_pair_t* item_pair, bool select);
+
+ virtual bool isSelected(item_pair_t* item_pair) const;
+
+ virtual bool removeItemPair(item_pair_t* item_pair);
+
+
+private:
+
+ void setItemsNoScrollWidth(S32 new_width) {mItemsNoScrollWidth = new_width - 2 * mBorderThickness;}
+
+
+private:
+
+ LLPanel* mItemsPanel;
+
+ S32 mItemsNoScrollWidth;
+
+ S32 mBorderThickness;
+
+ /** Items padding */
+ U32 mItemPad;
+
+ /** Selection support flag */
+ bool mAllowSelection;
+
+ /** Multiselection support flag, ignored if selection is not supported */
+ bool mMultipleSelection;
+
+ bool mKeepOneItemSelected;
+
+ /** All pairs of the list */
+ pairs_list_t mItemPairs;
+
+ /** Selected pairs for faster access */
+ pairs_list_t mSelectedItemPairs;
+};
+
+#endif
diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp
index c027b59c71..228e23b67e 100644
--- a/indra/llui/llfloater.cpp
+++ b/indra/llui/llfloater.cpp
@@ -1491,7 +1491,8 @@ LLFloater* LLFloater::getClosableFloaterFromFocus()
// The focused floater may not be closable,
// Find and close a parental floater that is closeable, if any.
- for(LLFloater* floater_to_close = focused_floater;
+ LLFloater* prev_floater = NULL;
+ for(LLFloater* floater_to_close = focused_floater;
NULL != floater_to_close;
floater_to_close = gFloaterView->getParentFloater(floater_to_close))
{
@@ -1499,6 +1500,14 @@ LLFloater* LLFloater::getClosableFloaterFromFocus()
{
return floater_to_close;
}
+
+ // If floater has as parent root view
+ // gFloaterView->getParentFloater(floater_to_close) returns
+ // the same floater_to_close, so we need to check this.
+ if (prev_floater == floater_to_close) {
+ break;
+ }
+ prev_floater = floater_to_close;
}
return NULL;
diff --git a/indra/llui/lllineeditor.h b/indra/llui/lllineeditor.h
index a024f48cc6..339aad30fb 100644
--- a/indra/llui/lllineeditor.h
+++ b/indra/llui/lllineeditor.h
@@ -189,6 +189,7 @@ public:
// Selects characters 'start' to 'end'.
void setSelection(S32 start, S32 end);
+ virtual void getSelectionRange(S32 *position, S32 *length) const;
void setCommitOnFocusLost( BOOL b ) { mCommitOnFocusLost = b; }
void setRevertOnEsc( BOOL b ) { mRevertOnEsc = b; }
@@ -277,7 +278,6 @@ private:
const segment_lengths_t &preedit_segment_lengths, const standouts_t &preedit_standouts, S32 caret_position);
virtual void markAsPreedit(S32 position, S32 length);
virtual void getPreeditRange(S32 *position, S32 *length) const;
- virtual void getSelectionRange(S32 *position, S32 *length) const;
virtual BOOL getPreeditLocation(S32 query_position, LLCoordGL *coord, LLRect *bounds, LLRect *control) const;
virtual S32 getPreeditFontSize() const;
diff --git a/indra/llui/llsearcheditor.cpp b/indra/llui/llsearcheditor.cpp
index fbcbb55b85..b87f645f3f 100644
--- a/indra/llui/llsearcheditor.cpp
+++ b/indra/llui/llsearcheditor.cpp
@@ -38,30 +38,68 @@
LLSearchEditor::LLSearchEditor(const LLSearchEditor::Params& p)
: LLUICtrl(p)
+ , mSearchButton(NULL)
+ , mClearButton(NULL)
{
- S32 btn_top = p.search_button.top_pad + p.search_button.rect.height;
- S32 btn_right = p.search_button.rect.width + p.search_button.left_pad;
- LLRect search_btn_rect(p.search_button.left_pad, btn_top, btn_right, p.search_button.top_pad);
+ S32 srch_btn_top = p.search_button.top_pad + p.search_button.rect.height;
+ S32 srch_btn_right = p.search_button.rect.width + p.search_button.left_pad;
+ LLRect srch_btn_rect(p.search_button.left_pad, srch_btn_top, srch_btn_right, p.search_button.top_pad);
+ S32 text_pad_left = p.text_pad_left;
+ if (p.search_button_visible)
+ text_pad_left += srch_btn_rect.getWidth();
+
+ // Set up line editor.
LLLineEditor::Params line_editor_params(p);
line_editor_params.name("filter edit box");
line_editor_params.rect(getLocalRect());
line_editor_params.follows.flags(FOLLOWS_ALL);
- line_editor_params.text_pad_left(p.text_pad_left + search_btn_rect.getWidth());
+ line_editor_params.text_pad_left(text_pad_left);
+ line_editor_params.revert_on_esc(false);
line_editor_params.commit_callback.function(boost::bind(&LLUICtrl::onCommit, this));
+ line_editor_params.keystroke_callback(boost::bind(&LLSearchEditor::handleKeystroke, this));
mSearchEditor = LLUICtrlFactory::create<LLLineEditor>(line_editor_params);
addChild(mSearchEditor);
- LLButton::Params button_params(p.search_button);
- button_params.name(std::string("clear filter"));
- button_params.rect(search_btn_rect) ;
- button_params.follows.flags(FOLLOWS_RIGHT|FOLLOWS_TOP);
- button_params.tab_stop(false);
- button_params.click_callback.function(boost::bind(&LLUICtrl::onCommit, this));
+ if (p.search_button_visible)
+ {
+ // Set up search button.
+ LLButton::Params srch_btn_params(p.search_button);
+ srch_btn_params.name(std::string("search button"));
+ srch_btn_params.rect(srch_btn_rect) ;
+ srch_btn_params.follows.flags(FOLLOWS_LEFT|FOLLOWS_TOP);
+ srch_btn_params.tab_stop(false);
+ srch_btn_params.click_callback.function(boost::bind(&LLUICtrl::onCommit, this));
+
+ mSearchButton = LLUICtrlFactory::create<LLButton>(srch_btn_params);
+ mSearchEditor->addChild(mSearchButton);
+ }
+
+ if (p.clear_button_visible)
+ {
+ // Set up clear button.
+ S32 clr_btn_width = getRect().getHeight(); // button is square, and as tall as search editor
+ LLRect clear_btn_rect(getRect().getWidth() - clr_btn_width, getRect().getHeight(), getRect().getWidth(), 0);
+ LLButton::Params clr_btn_params(p.clear_button);
+ clr_btn_params.name(std::string("clear button"));
+ clr_btn_params.rect(clear_btn_rect) ;
+ clr_btn_params.follows.flags(FOLLOWS_RIGHT|FOLLOWS_TOP);
+ clr_btn_params.tab_stop(false);
+ clr_btn_params.click_callback.function(boost::bind(&LLSearchEditor::onClearButtonClick, this, _2));
+
+ mClearButton = LLUICtrlFactory::create<LLButton>(clr_btn_params);
+ mSearchEditor->addChild(mClearButton);
+ }
+}
+
+//virtual
+void LLSearchEditor::draw()
+{
+ if (mClearButton)
+ mClearButton->setVisible(!mSearchEditor->getWText().empty());
- mSearchButton = LLUICtrlFactory::create<LLButton>(button_params);
- mSearchEditor->addChild(mSearchButton);
+ LLUICtrl::draw();
}
//virtual
@@ -89,6 +127,12 @@ BOOL LLSearchEditor::setLabelArg( const std::string& key, const LLStringExplicit
}
//virtual
+void LLSearchEditor::setLabel( const LLStringExplicit &new_label )
+{
+ mSearchEditor->setLabel(new_label);
+}
+
+//virtual
void LLSearchEditor::clear()
{
if (mSearchEditor)
@@ -96,3 +140,17 @@ void LLSearchEditor::clear()
mSearchEditor->clear();
}
}
+
+void LLSearchEditor::onClearButtonClick(const LLSD& data)
+{
+ setText(LLStringUtil::null);
+ mSearchEditor->doDelete(); // force keystroke callback
+}
+
+void LLSearchEditor::handleKeystroke()
+{
+ if (mKeystrokeCallback)
+ {
+ mKeystrokeCallback(this, getValue());
+ }
+}
diff --git a/indra/llui/llsearcheditor.h b/indra/llui/llsearcheditor.h
index cd2867b493..f395e7e816 100644
--- a/indra/llui/llsearcheditor.h
+++ b/indra/llui/llsearcheditor.h
@@ -50,10 +50,15 @@ class LLSearchEditor : public LLUICtrl
public:
struct Params : public LLInitParam::Block<Params, LLLineEditor::Params>
{
- Optional<LLButton::Params> search_button;
+ Optional<LLButton::Params> search_button, clear_button;
+ Optional<bool> search_button_visible, clear_button_visible;
+ Optional<commit_callback_t> keystroke_callback;
Params()
: search_button("search_button")
+ , search_button_visible("search_button_visible")
+ , clear_button("clear_button")
+ , clear_button_visible("clear_button_visible")
{
name = "search_editor";
}
@@ -66,26 +71,29 @@ protected:
public:
virtual ~LLSearchEditor() {}
+ /*virtual*/ void draw();
+
void setText(const LLStringExplicit &new_text) { mSearchEditor->setText(new_text); }
const std::string& getText() const { return mSearchEditor->getText(); }
-
// LLUICtrl interface
virtual void setValue(const LLSD& value );
virtual LLSD getValue() const;
virtual BOOL setTextArg( const std::string& key, const LLStringExplicit& text );
virtual BOOL setLabelArg( const std::string& key, const LLStringExplicit& text );
+ virtual void setLabel( const LLStringExplicit &new_label );
virtual void clear();
- void setKeystrokeCallback(LLLineEditor::callback_t callback, void* user_data)
- {
- if(mSearchEditor)
- mSearchEditor->setKeystrokeCallback(callback,user_data);
- }
+ void setKeystrokeCallback( commit_callback_t cb ) { mKeystrokeCallback = cb; }
+
+protected:
+ void onClearButtonClick(const LLSD& data);
+ virtual void handleKeystroke();
-private:
+ commit_callback_t mKeystrokeCallback;
LLLineEditor* mSearchEditor;
LLButton* mSearchButton;
+ LLButton* mClearButton;
};
#endif // LL_SEARCHEDITOR_H
diff --git a/indra/llui/lltabcontainer.cpp b/indra/llui/lltabcontainer.cpp
index cabd0be522..720ca692f7 100644
--- a/indra/llui/lltabcontainer.cpp
+++ b/indra/llui/lltabcontainer.cpp
@@ -306,7 +306,7 @@ void LLTabContainer::draw()
setScrollPosPixels((S32)lerp((F32)getScrollPosPixels(), (F32)target_pixel_scroll, LLCriticalDamp::getInterpolant(0.08f)));
- BOOL has_scroll_arrows = (mMaxScrollPos > 0) || (mScrollPosPixels > 0);
+ BOOL has_scroll_arrows = !getTabsHidden() && ((mMaxScrollPos > 0) || (mScrollPosPixels > 0));
if (!mIsVertical)
{
mJumpPrevArrowBtn->setVisible( has_scroll_arrows );
@@ -429,7 +429,7 @@ BOOL LLTabContainer::handleMouseDown( S32 x, S32 y, MASK mask )
{
static LLUICachedControl<S32> tabcntrv_pad ("UITabCntrvPad", 0);
BOOL handled = FALSE;
- BOOL has_scroll_arrows = (getMaxScrollPos() > 0);
+ BOOL has_scroll_arrows = (getMaxScrollPos() > 0) && !getTabsHidden();
if (has_scroll_arrows)
{
@@ -498,7 +498,7 @@ BOOL LLTabContainer::handleMouseDown( S32 x, S32 y, MASK mask )
BOOL LLTabContainer::handleHover( S32 x, S32 y, MASK mask )
{
BOOL handled = FALSE;
- BOOL has_scroll_arrows = (getMaxScrollPos() > 0);
+ BOOL has_scroll_arrows = (getMaxScrollPos() > 0) && !getTabsHidden();
if (has_scroll_arrows)
{
@@ -540,7 +540,7 @@ BOOL LLTabContainer::handleHover( S32 x, S32 y, MASK mask )
BOOL LLTabContainer::handleMouseUp( S32 x, S32 y, MASK mask )
{
BOOL handled = FALSE;
- BOOL has_scroll_arrows = (getMaxScrollPos() > 0);
+ BOOL has_scroll_arrows = (getMaxScrollPos() > 0) && !getTabsHidden();
if (has_scroll_arrows)
{
@@ -1840,12 +1840,11 @@ void LLTabContainer::updateMaxScrollPos()
void LLTabContainer::commitHoveredButton(S32 x, S32 y)
{
- if (hasMouseCapture())
+ if (!getTabsHidden() && hasMouseCapture())
{
for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
{
LLTabTuple* tuple = *iter;
- tuple->mButton->setVisible( TRUE );
S32 local_x = x - tuple->mButton->getRect().mLeft;
S32 local_y = y - tuple->mButton->getRect().mBottom;
if (tuple->mButton->pointInView(local_x, local_y) && tuple->mButton->getEnabled() && !tuple->mTabPanel->getVisible())
diff --git a/indra/llui/llurlentry.cpp b/indra/llui/llurlentry.cpp
index 85f9064115..c20212c375 100644
--- a/indra/llui/llurlentry.cpp
+++ b/indra/llui/llurlentry.cpp
@@ -339,54 +339,75 @@ std::string LLUrlEntryGroup::getLabel(const std::string &url, const LLUrlLabelCa
}
///
-/// LLUrlEntryEvent Describes a Second Life event Url, e.g.,
-/// secondlife:///app/event/700727/about
+/// LLUrlEntryParcel Describes a Second Life parcel Url, e.g.,
+/// secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about
///
-LLUrlEntryEvent::LLUrlEntryEvent()
+LLUrlEntryParcel::LLUrlEntryParcel()
{
- mPattern = boost::regex("secondlife:///app/event/[\\da-f-]+/about",
+ mPattern = boost::regex("secondlife:///app/parcel/[\\da-f-]+/about",
boost::regex::perl|boost::regex::icase);
- mMenuName = "menu_url_event.xml";
- mTooltip = LLTrans::getString("TooltipEventUrl");
+ mMenuName = "menu_url_parcel.xml";
+ mTooltip = LLTrans::getString("TooltipParcelUrl");
}
-std::string LLUrlEntryEvent::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+std::string LLUrlEntryParcel::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
{
return unescapeUrl(url);
}
-///
-/// LLUrlEntryClassified Describes a Second Life classified Url, e.g.,
-/// secondlife:///app/classified/00128854-c36a-5649-7ca6-5dfaa7514ab2/about
-///
-LLUrlEntryClassified::LLUrlEntryClassified()
+//
+// LLUrlEntryPlace Describes secondlife:///<location> URLs
+//
+LLUrlEntryPlace::LLUrlEntryPlace()
{
- mPattern = boost::regex("secondlife:///app/classified/[\\da-f-]+/about",
+ mPattern = boost::regex("secondlife://\\S+/?(\\d+/\\d+/\\d+|\\d+/\\d+)/?",
boost::regex::perl|boost::regex::icase);
- mMenuName = "menu_url_classified.xml";
- mTooltip = LLTrans::getString("TooltipClassifiedUrl");
+ mMenuName = "menu_url_slurl.xml";
+ mTooltip = LLTrans::getString("TooltipSLURL");
}
-std::string LLUrlEntryClassified::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+std::string LLUrlEntryPlace::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
{
- return unescapeUrl(url);
-}
+ //
+ // we handle SLURLs in the following formats:
+ // - secondlife://Place/X/Y/Z
+ // - secondlife://Place/X/Y
+ //
+ LLURI uri(url);
+ std::string location = unescapeUrl(uri.hostName());
+ LLSD path_array = uri.pathArray();
+ S32 path_parts = path_array.size();
+ if (path_parts == 3)
+ {
+ // handle slurl with (X,Y,Z) coordinates
+ std::string x = path_array[0];
+ std::string y = path_array[1];
+ std::string z = path_array[2];
+ return location + " (" + x + "," + y + "," + z + ")";
+ }
+ else if (path_parts == 2)
+ {
+ // handle slurl with (X,Y) coordinates
+ std::string x = path_array[0];
+ std::string y = path_array[1];
+ return location + " (" + x + "," + y + ")";
+ }
-///
-/// LLUrlEntryParcel Describes a Second Life parcel Url, e.g.,
-/// secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about
-///
-LLUrlEntryParcel::LLUrlEntryParcel()
-{
- mPattern = boost::regex("secondlife:///app/parcel/[\\da-f-]+/about",
- boost::regex::perl|boost::regex::icase);
- mMenuName = "menu_url_parcel.xml";
- mTooltip = LLTrans::getString("TooltipParcelUrl");
+ return url;
}
-std::string LLUrlEntryParcel::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+std::string LLUrlEntryPlace::getLocation(const std::string &url) const
{
- return unescapeUrl(url);
+ // return the part of the Url after secondlife:// part
+ const std::string search_string = "://";
+ size_t pos = url.find(search_string);
+ if (pos == std::string::npos)
+ {
+ return "";
+ }
+
+ pos += search_string.size();
+ return url.substr(pos, url.size() - pos);
}
//
diff --git a/indra/llui/llurlentry.h b/indra/llui/llurlentry.h
index f3e76dbec0..54053872df 100644
--- a/indra/llui/llurlentry.h
+++ b/indra/llui/llurlentry.h
@@ -170,36 +170,26 @@ private:
};
///
-/// LLUrlEntryEvent Describes a Second Life event Url, e.g.,
-/// secondlife:///app/event/700727/about
-///
-class LLUrlEntryEvent : public LLUrlEntryBase
-{
-public:
- LLUrlEntryEvent();
- /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
-};
-
-///
-/// LLUrlEntryClassified Describes a Second Life classified Url, e.g.,
-/// secondlife:///app/classified/00128854-c36a-5649-7ca6-5dfaa7514ab2/about
+/// LLUrlEntryParcel Describes a Second Life parcel Url, e.g.,
+/// secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about
///
-class LLUrlEntryClassified : public LLUrlEntryBase
+class LLUrlEntryParcel : public LLUrlEntryBase
{
public:
- LLUrlEntryClassified();
+ LLUrlEntryParcel();
/*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
};
///
-/// LLUrlEntryParcel Describes a Second Life parcel Url, e.g.,
-/// secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about
+/// LLUrlEntryPlace Describes a Second Life location Url, e.g.,
+/// secondlife:///Ahern/50/50/50
///
-class LLUrlEntryParcel : public LLUrlEntryBase
+class LLUrlEntryPlace : public LLUrlEntryBase
{
public:
- LLUrlEntryParcel();
+ LLUrlEntryPlace();
/*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
+ /*virtual*/ std::string getLocation(const std::string &url) const;
};
///
diff --git a/indra/llui/llurlregistry.cpp b/indra/llui/llurlregistry.cpp
index 938375ad13..f2d340deb7 100644
--- a/indra/llui/llurlregistry.cpp
+++ b/indra/llui/llurlregistry.cpp
@@ -49,11 +49,10 @@ LLUrlRegistry::LLUrlRegistry()
registerUrl(new LLUrlEntryHTTPLabel());
registerUrl(new LLUrlEntryAgent());
registerUrl(new LLUrlEntryGroup());
- registerUrl(new LLUrlEntryEvent());
- registerUrl(new LLUrlEntryClassified());
registerUrl(new LLUrlEntryParcel());
registerUrl(new LLUrlEntryTeleport());
registerUrl(new LLUrlEntryObjectIM());
+ registerUrl(new LLUrlEntryPlace());
registerUrl(new LLUrlEntrySL());
registerUrl(new LLUrlEntrySLLabel());
}
@@ -118,8 +117,8 @@ static bool matchRegex(const char *text, boost::regex regex, U32 &start, U32 &en
bool LLUrlRegistry::findUrl(const std::string &text, LLUrlMatch &match, const LLUrlLabelCallback &cb)
{
- // test for the trivial case of no text and get out fast
- if (text.empty())
+ // avoid costly regexes if there is clearly no URL in the text
+ if (text.find("://") == std::string::npos)
{
return false;
}
diff --git a/indra/llui/llview.h b/indra/llui/llview.h
index d80c2af568..1f7e5afaae 100644
--- a/indra/llui/llview.h
+++ b/indra/llui/llview.h
@@ -633,7 +633,7 @@ template <class T> T* LLView::getChild(const std::string& name, BOOL recurse) co
// did we find *something* with that name?
if (child)
{
- llwarns << "Found child named " << name << " but of wrong type " << typeid(child).name() << ", expecting " << typeid(T*).name() << llendl;
+ llwarns << "Found child named " << name << " but of wrong type " << typeid(*child).name() << ", expecting " << typeid(T*).name() << llendl;
}
result = getDefaultWidget<T>(name);
if (!result)
diff --git a/indra/llui/tests/llurlentry_test.cpp b/indra/llui/tests/llurlentry_test.cpp
index 610ee3349b..1e7a0f7f2c 100644
--- a/indra/llui/tests/llurlentry_test.cpp
+++ b/indra/llui/tests/llurlentry_test.cpp
@@ -308,56 +308,52 @@ namespace tut
void object::test<6>()
{
//
- // test LLUrlEntryEvent - secondlife://app/event Urls
+ // test LLUrlEntryPlace - secondlife://<location> URLs
//
- LLUrlEntryEvent url;
+ LLUrlEntryPlace url;
boost::regex r = url.getPattern();
- testRegex("Invalid Event Url", r,
- "secondlife:///app/event/FOO/about",
+ testRegex("no valid slurl [1]", r,
+ "secondlife://Ahern/FOO/50/",
"");
- testRegex("Event Url ", r,
- "secondlife:///app/event/700727/about",
- "secondlife:///app/event/700727/about");
+ testRegex("Ahern (50,50,50) [1]", r,
+ "secondlife://Ahern/50/50/50/",
+ "secondlife://Ahern/50/50/50/");
- testRegex("Event Url in text", r,
- "XXX secondlife:///app/event/700727/about XXX",
- "secondlife:///app/event/700727/about");
+ testRegex("Ahern (50,50,50) [2]", r,
+ "XXX secondlife://Ahern/50/50/50/ XXX",
+ "secondlife://Ahern/50/50/50/");
- testRegex("Event Url multicase", r,
- "XXX secondlife:///APP/Event/700727/about XXX",
- "secondlife:///APP/Event/700727/about");
- }
+ testRegex("Ahern (50,50,50) [3]", r,
+ "XXX secondlife://Ahern/50/50/50 XXX",
+ "secondlife://Ahern/50/50/50");
- template<> template<>
- void object::test<7>()
- {
- //
- // test LLUrlEntryClassified - secondlife://app/classified Urls
- //
- LLUrlEntryClassified url;
- boost::regex r = url.getPattern();
+ testRegex("Ahern (50,50,50) multicase", r,
+ "XXX SecondLife://Ahern/50/50/50/ XXX",
+ "SecondLife://Ahern/50/50/50/");
- testRegex("Invalid Classified Url", r,
- "secondlife:///app/classified/00128854-XXXX-5649-7ca6-5dfaa7514ab2/about",
- "");
+ testRegex("Ahern (50,50) [1]", r,
+ "XXX secondlife://Ahern/50/50/ XXX",
+ "secondlife://Ahern/50/50/");
- testRegex("Classified Url ", r,
- "secondlife:///app/classified/00128854-c36a-5649-7ca6-5dfaa7514ab2/about",
- "secondlife:///app/classified/00128854-c36a-5649-7ca6-5dfaa7514ab2/about");
+ testRegex("Ahern (50,50) [2]", r,
+ "XXX secondlife://Ahern/50/50 XXX",
+ "secondlife://Ahern/50/50");
- testRegex("Classified Url in text", r,
- "XXX secondlife:///app/classified/00128854-c36a-5649-7ca6-5dfaa7514ab2/about XXX",
- "secondlife:///app/classified/00128854-c36a-5649-7ca6-5dfaa7514ab2/about");
+ // DEV-21577: In-world SLURLs containing "(" or ")" are not treated as a hyperlink in chat
+ testRegex("SLURL with brackets", r,
+ "XXX secondlife://Burning%20Life%20(Hyper)/27/210/30 XXX",
+ "secondlife://Burning%20Life%20(Hyper)/27/210/30");
- testRegex("Classified Url multicase", r,
- "XXX secondlife:///APP/Classified/00128854-c36a-5649-7ca6-5dfaa7514ab2/About XXX",
- "secondlife:///APP/Classified/00128854-c36a-5649-7ca6-5dfaa7514ab2/About");
+ // DEV-35459: SLURLs and teleport Links not parsed properly
+ testRegex("SLURL with quote", r,
+ "XXX secondlife://A'ksha%20Oasis/41/166/701 XXX",
+ "secondlife://A'ksha%20Oasis/41/166/701");
}
template<> template<>
- void object::test<8>()
+ void object::test<7>()
{
//
// test LLUrlEntryParcel - secondlife://app/parcel Urls
@@ -382,7 +378,7 @@ namespace tut
"secondlife:///APP/Parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/About");
}
template<> template<>
- void object::test<9>()
+ void object::test<8>()
{
//
// test LLUrlEntryTeleport - secondlife://app/teleport URLs
@@ -458,7 +454,7 @@ namespace tut
}
template<> template<>
- void object::test<10>()
+ void object::test<9>()
{
//
// test LLUrlEntrySL - general secondlife:// URLs
@@ -496,7 +492,7 @@ namespace tut
}
template<> template<>
- void object::test<11>()
+ void object::test<10>()
{
//
// test LLUrlEntrySLLabel - general secondlife:// URLs with labels