summaryrefslogtreecommitdiff
path: root/indra/llui
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llui')
-rw-r--r--indra/llui/CMakeLists.txt14
-rw-r--r--indra/llui/llaccordionctrl.cpp220
-rw-r--r--indra/llui/llaccordionctrl.h13
-rw-r--r--indra/llui/llaccordionctrltab.cpp353
-rw-r--r--indra/llui/llaccordionctrltab.h37
-rw-r--r--indra/llui/llbutton.cpp38
-rw-r--r--indra/llui/llbutton.h29
-rw-r--r--indra/llui/llcombobox.cpp78
-rw-r--r--indra/llui/llcombobox.h36
-rw-r--r--indra/llui/llconsole.cpp66
-rw-r--r--indra/llui/llconsole.h13
-rw-r--r--indra/llui/lldockablefloater.cpp10
-rw-r--r--indra/llui/lldockcontrol.cpp47
-rw-r--r--indra/llui/lldockcontrol.h5
-rw-r--r--indra/llui/lldraghandle.cpp8
-rw-r--r--indra/llui/lldraghandle.h3
-rw-r--r--indra/llui/lleditmenuhandler.cpp7
-rw-r--r--indra/llui/lleditmenuhandler.h2
-rw-r--r--indra/llui/llflatlistview.cpp230
-rw-r--r--indra/llui/llflatlistview.h75
-rw-r--r--indra/llui/llfloater.cpp174
-rw-r--r--indra/llui/llfloater.h26
-rw-r--r--indra/llui/llfloaterreg.cpp17
-rw-r--r--indra/llui/llfloaterreg.h4
-rw-r--r--indra/llui/llfocusmgr.cpp51
-rw-r--r--indra/llui/llfocusmgr.h10
-rw-r--r--indra/llui/llhandle.h7
-rw-r--r--indra/llui/lliconctrl.h1
-rw-r--r--indra/llui/llkeywords.cpp82
-rw-r--r--indra/llui/llkeywords.h29
-rw-r--r--indra/llui/lllayoutstack.cpp43
-rw-r--r--indra/llui/lllayoutstack.h10
-rw-r--r--indra/llui/lllineeditor.cpp445
-rw-r--r--indra/llui/lllineeditor.h42
-rw-r--r--indra/llui/llloadingindicator.cpp133
-rw-r--r--indra/llui/llloadingindicator.h93
-rw-r--r--indra/llui/llmenugl.cpp73
-rw-r--r--indra/llui/llmenugl.h14
-rw-r--r--indra/llui/llmodaldialog.cpp6
-rw-r--r--indra/llui/llmultifloater.cpp17
-rw-r--r--indra/llui/llmultisliderctrl.cpp9
-rw-r--r--indra/llui/llnotifications.cpp209
-rw-r--r--indra/llui/llnotifications.h112
-rw-r--r--indra/llui/llnotificationsutil.cpp5
-rw-r--r--indra/llui/llnotificationsutil.h2
-rw-r--r--indra/llui/llpanel.cpp20
-rw-r--r--indra/llui/llpanel.h10
-rw-r--r--indra/llui/llradiogroup.cpp8
-rw-r--r--indra/llui/llresizehandle.cpp86
-rw-r--r--indra/llui/llscrollingpanellist.cpp28
-rw-r--r--indra/llui/llscrollingpanellist.h6
-rw-r--r--indra/llui/llscrolllistcell.cpp4
-rw-r--r--indra/llui/llscrolllistcell.h5
-rw-r--r--indra/llui/llscrolllistctrl.cpp53
-rw-r--r--indra/llui/llscrolllistctrl.h41
-rw-r--r--indra/llui/llscrolllistitem.h2
-rw-r--r--indra/llui/llsearcheditor.cpp16
-rw-r--r--indra/llui/llsearcheditor.h2
-rw-r--r--indra/llui/llsliderctrl.cpp10
-rw-r--r--indra/llui/llsliderctrl.h2
-rw-r--r--indra/llui/llspinctrl.cpp23
-rw-r--r--indra/llui/llspinctrl.h6
-rw-r--r--indra/llui/llstyle.cpp5
-rw-r--r--indra/llui/llstyle.h4
-rw-r--r--indra/llui/lltabcontainer.cpp232
-rw-r--r--indra/llui/lltabcontainer.h29
-rw-r--r--indra/llui/lltextbase.cpp248
-rw-r--r--indra/llui/lltextbase.h38
-rw-r--r--indra/llui/lltexteditor.cpp399
-rw-r--r--indra/llui/lltexteditor.h23
-rw-r--r--indra/llui/lltextvalidate.cpp302
-rw-r--r--indra/llui/lltextvalidate.h63
-rw-r--r--indra/llui/lltooltip.cpp9
-rw-r--r--indra/llui/lltooltip.h5
-rw-r--r--indra/llui/llui.cpp190
-rw-r--r--indra/llui/llui.h18
-rw-r--r--indra/llui/lluicolortable.cpp29
-rw-r--r--indra/llui/lluicolortable.h9
-rw-r--r--indra/llui/lluictrlfactory.cpp5
-rw-r--r--indra/llui/lluifwd.h1
-rw-r--r--indra/llui/lluiimage.cpp4
-rw-r--r--indra/llui/lluiimage.h1
-rw-r--r--indra/llui/lluistring.h2
-rw-r--r--indra/llui/llurlaction.cpp17
-rw-r--r--indra/llui/llurlaction.h3
-rw-r--r--indra/llui/llurlentry.cpp180
-rw-r--r--indra/llui/llurlentry.h58
-rw-r--r--indra/llui/llurlmatch.cpp8
-rw-r--r--indra/llui/llurlmatch.h6
-rw-r--r--indra/llui/llurlregistry.cpp25
-rw-r--r--indra/llui/llview.cpp20
-rw-r--r--indra/llui/llview.h4
-rw-r--r--indra/llui/llviewborder.cpp58
-rw-r--r--indra/llui/llviewborder.h3
-rw-r--r--indra/llui/llviewmodel.h3
-rw-r--r--indra/llui/tests/llurlentry_test.cpp307
-rw-r--r--indra/llui/tests/llurlmatch_test.cpp31
97 files changed, 3750 insertions, 1809 deletions
diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt
index ce068618e2..3ecab90756 100644
--- a/indra/llui/CMakeLists.txt
+++ b/indra/llui/CMakeLists.txt
@@ -52,6 +52,7 @@ set(llui_SOURCE_FILES
llkeywords.cpp
lllayoutstack.cpp
lllineeditor.cpp
+ llloadingindicator.cpp
lllocalcliprect.cpp
llmenubutton.cpp
llmenugl.cpp
@@ -90,6 +91,7 @@ set(llui_SOURCE_FILES
lltextbox.cpp
lltexteditor.cpp
lltextparser.cpp
+ lltextvalidate.cpp
lltransutil.cpp
lltoggleablemenu.cpp
lltooltip.cpp
@@ -143,6 +145,7 @@ set(llui_HEADER_FILES
lllayoutstack.h
lllazyvalue.h
lllineeditor.h
+ llloadingindicator.h
lllocalcliprect.h
llmenubutton.h
llmenugl.h
@@ -182,6 +185,7 @@ set(llui_HEADER_FILES
lltextbox.h
lltexteditor.h
lltextparser.h
+ lltextvalidate.h
lltoggleablemenu.h
lltooltip.h
lltransutil.h
@@ -207,6 +211,16 @@ set(llui_HEADER_FILES
set_source_files_properties(${llui_HEADER_FILES}
PROPERTIES HEADER_FILE_ONLY TRUE)
+SET(llurlentry_TEST_DEPENDENCIES
+ llurlmatch.cpp
+ llurlregistry.cpp
+ )
+
+set_source_files_properties(llurlentry.cpp
+ PROPERTIES LL_TEST_ADDITIONAL_SOURCE_FILES
+ "${llurlentry_TEST_DEPENDENCIES}"
+ )
+
list(APPEND llui_SOURCE_FILES ${llui_HEADER_FILES})
add_library (llui ${llui_SOURCE_FILES})
diff --git a/indra/llui/llaccordionctrl.cpp b/indra/llui/llaccordionctrl.cpp
index b5e870228a..5d1d57cbb2 100644
--- a/indra/llui/llaccordionctrl.cpp
+++ b/indra/llui/llaccordionctrl.cpp
@@ -63,6 +63,8 @@ static LLDefaultChildRegistry::Register<LLAccordionCtrl> t2("accordion");
LLAccordionCtrl::LLAccordionCtrl(const Params& params):LLPanel(params)
, mFitParent(params.fit_parent)
+ , mAutoScrolling( false )
+ , mAutoScrollRate( 0.f )
{
mSingleExpansion = params.single_expansion;
if(mFitParent && !mSingleExpansion)
@@ -72,6 +74,8 @@ LLAccordionCtrl::LLAccordionCtrl(const Params& params):LLPanel(params)
}
LLAccordionCtrl::LLAccordionCtrl() : LLPanel()
+ , mAutoScrolling( false )
+ , mAutoScrollRate( 0.f )
{
mSingleExpansion = false;
mFitParent = false;
@@ -81,6 +85,19 @@ LLAccordionCtrl::LLAccordionCtrl() : LLPanel()
//---------------------------------------------------------------------------------
void LLAccordionCtrl::draw()
{
+ if (mAutoScrolling)
+ {
+ // add acceleration to autoscroll
+ mAutoScrollRate = llmin(mAutoScrollRate + (LLFrameTimer::getFrameDeltaTimeF32() * AUTO_SCROLL_RATE_ACCEL), MAX_AUTO_SCROLL_RATE);
+ }
+ else
+ {
+ // reset to minimum for next time
+ mAutoScrollRate = MIN_AUTO_SCROLL_RATE;
+ }
+ // clear this flag to be set on next call to autoScroll
+ mAutoScrolling = false;
+
LLRect local_rect(0, getRect().getHeight(), getRect().getWidth(), 0);
LLLocalClipRect clip(local_rect);
@@ -101,7 +118,6 @@ BOOL LLAccordionCtrl::postBuild()
scrollbar_size,
getRect().getHeight() - 1);
-
LLScrollbar::Params sbparams;
sbparams.name("scrollable vertical");
sbparams.rect(scroll_rect);
@@ -313,44 +329,105 @@ void LLAccordionCtrl::addCollapsibleCtrl(LLView* view)
LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(view);
if(!accordion_tab)
return;
- if(std::find(getChildList()->begin(),getChildList()->end(),accordion_tab) == getChildList()->end())
+ if(std::find(beginChild(), endChild(), accordion_tab) == endChild())
addChild(accordion_tab);
mAccordionTabs.push_back(accordion_tab);
-
+
accordion_tab->setDropDownStateChangedCallback( boost::bind(&LLAccordionCtrl::onCollapseCtrlCloseOpen, this, mAccordionTabs.size() - 1) );
}
-
-void LLAccordionCtrl::arrange()
+void LLAccordionCtrl::removeCollapsibleCtrl(LLView* view)
{
- if( mAccordionTabs.size() == 0)
- {
- //We do not arrange if we do not have what should be arranged
+ LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(view);
+ if(!accordion_tab)
return;
+
+ if(std::find(beginChild(), endChild(), accordion_tab) != endChild())
+ removeChild(accordion_tab);
+
+ for (std::vector<LLAccordionCtrlTab*>::iterator iter = mAccordionTabs.begin();
+ iter != mAccordionTabs.end(); ++iter)
+ {
+ if (accordion_tab == (*iter))
+ {
+ mAccordionTabs.erase(iter);
+ break;
+ }
}
+}
- //Calculate params
+void LLAccordionCtrl::arrangeSinge()
+{
S32 panel_left = BORDER_MARGIN; // Margin from left side of Splitter
S32 panel_top = getRect().getHeight() - BORDER_MARGIN; // Top coordinate of the first panel
S32 panel_width = getRect().getWidth() - 4; // Top coordinate of the first panel
+ S32 panel_height;
-
- if(mAccordionTabs.size() == 1)
+ S32 collapsed_height = 0;
+
+ for(size_t i=0;i<mAccordionTabs.size();++i)
{
- LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[0]);
-
- LLRect panel_rect = accordion_tab->getRect();
+ LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[i]);
- S32 panel_height = getRect().getHeight() - 2*BORDER_MARGIN;
+ if(accordion_tab->getVisible() == false) //skip hidden accordion tabs
+ continue;
+ if(!accordion_tab->isExpanded() )
+ {
+ collapsed_height+=mAccordionTabs[i]->getRect().getHeight();
+ }
+ }
- ctrlSetLeftTopAndSize(accordion_tab,panel_rect.mLeft,panel_top,panel_width,panel_height);
+ S32 expanded_height = getRect().getHeight() - BORDER_MARGIN - collapsed_height;
+
+ for(size_t i=0;i<mAccordionTabs.size();++i)
+ {
+ LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[i]);
- show_hide_scrollbar(getRect().getWidth(),getRect().getHeight());
- return;
+ if(accordion_tab->getVisible() == false) //skip hidden accordion tabs
+ continue;
+ if(!accordion_tab->isExpanded() )
+ {
+ panel_height = accordion_tab->getRect().getHeight();
+ }
+ else
+ {
+ if(mFitParent)
+ {
+ panel_height = expanded_height;
+ }
+ else
+ {
+ if(accordion_tab->getAccordionView())
+ {
+ panel_height = accordion_tab->getAccordionView()->getRect().getHeight() +
+ accordion_tab->getHeaderHeight() + 2*BORDER_MARGIN;
+ }
+ else
+ {
+ panel_height = accordion_tab->getRect().getHeight();
+ }
+ }
+ }
+ // make sure at least header is shown
+ panel_height = llmax(panel_height, accordion_tab->getHeaderHeight());
+
+ ctrlSetLeftTopAndSize(mAccordionTabs[i], panel_left, panel_top, panel_width, panel_height);
+ panel_top-=mAccordionTabs[i]->getRect().getHeight();
}
+ show_hide_scrollbar(getRect().getWidth(), getRect().getHeight());
+ updateLayout(getRect().getWidth(), getRect().getHeight());
+}
+
+void LLAccordionCtrl::arrangeMultiple()
+{
+ S32 panel_left = BORDER_MARGIN; // Margin from left side of Splitter
+ S32 panel_top = getRect().getHeight() - BORDER_MARGIN; // Top coordinate of the first panel
+ S32 panel_width = getRect().getWidth() - 4; // Top coordinate of the first panel
+
+ //Calculate params
for(size_t i = 0; i < mAccordionTabs.size(); i++ )
{
LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[i]);
@@ -398,7 +475,40 @@ void LLAccordionCtrl::arrange()
show_hide_scrollbar(getRect().getWidth(),getRect().getHeight());
updateLayout(getRect().getWidth(),getRect().getHeight());
+}
+
+
+void LLAccordionCtrl::arrange()
+{
+ if( mAccordionTabs.size() == 0)
+ {
+ //We do not arrange if we do not have what should be arranged
+ return;
+ }
+
+
+ if(mAccordionTabs.size() == 1)
+ {
+ S32 panel_top = getRect().getHeight() - BORDER_MARGIN; // Top coordinate of the first panel
+ S32 panel_width = getRect().getWidth() - 4; // Top coordinate of the first panel
+
+ LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[0]);
+
+ LLRect panel_rect = accordion_tab->getRect();
+
+ S32 panel_height = getRect().getHeight() - 2*BORDER_MARGIN;
+
+ ctrlSetLeftTopAndSize(accordion_tab,panel_rect.mLeft,panel_top,panel_width,panel_height);
+
+ show_hide_scrollbar(getRect().getWidth(),getRect().getHeight());
+ return;
+ }
+
+ if(mSingleExpansion)
+ arrangeSinge ();
+ else
+ arrangeMultiple ();
}
//---------------------------------------------------------------------------------
@@ -420,6 +530,64 @@ BOOL LLAccordionCtrl::handleKeyHere (KEY key, MASK mask)
return LLPanel::handleKeyHere(key,mask);
}
+BOOL LLAccordionCtrl::handleDragAndDrop (S32 x, S32 y, MASK mask,
+ BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ std::string& tooltip_msg)
+{
+ // Scroll folder view if needed. Never accepts a drag or drop.
+ *accept = ACCEPT_NO;
+ BOOL handled = autoScroll(x, y);
+
+ if( !handled )
+ {
+ handled = childrenHandleDragAndDrop(x, y, mask, drop, cargo_type,
+ cargo_data, accept, tooltip_msg) != NULL;
+ }
+ return TRUE;
+}
+
+BOOL LLAccordionCtrl::autoScroll (S32 x, S32 y)
+{
+ static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
+
+ bool scrolling = false;
+ if( mScrollbar->getVisible() )
+ {
+ LLRect rect_local( 0, getRect().getHeight(), getRect().getWidth() - scrollbar_size, 0 );
+ LLRect screen_local_extents;
+
+ // clip rect against root view
+ screenRectToLocal(getRootView()->getLocalRect(), &screen_local_extents);
+ rect_local.intersectWith(screen_local_extents);
+
+ // autoscroll region should take up no more than one third of visible scroller area
+ S32 auto_scroll_region_height = llmin(rect_local.getHeight() / 3, 10);
+ S32 auto_scroll_speed = llround(mAutoScrollRate * LLFrameTimer::getFrameDeltaTimeF32());
+
+ LLRect bottom_scroll_rect = screen_local_extents;
+ bottom_scroll_rect.mTop = rect_local.mBottom + auto_scroll_region_height;
+ if( bottom_scroll_rect.pointInRect( x, y ) && (mScrollbar->getDocPos() < mScrollbar->getDocPosMax()) )
+ {
+ mScrollbar->setDocPos( mScrollbar->getDocPos() + auto_scroll_speed );
+ mAutoScrolling = true;
+ scrolling = true;
+ }
+
+ LLRect top_scroll_rect = screen_local_extents;
+ top_scroll_rect.mBottom = rect_local.mTop - auto_scroll_region_height;
+ if( top_scroll_rect.pointInRect( x, y ) && (mScrollbar->getDocPos() > 0) )
+ {
+ mScrollbar->setDocPos( mScrollbar->getDocPos() - auto_scroll_speed );
+ mAutoScrolling = true;
+ scrolling = true;
+ }
+ }
+ return scrolling;
+}
+
void LLAccordionCtrl::updateLayout (S32 width, S32 height)
{
S32 panel_top = height - BORDER_MARGIN ;
@@ -500,15 +668,23 @@ S32 LLAccordionCtrl::notifyParent(const LLSD& info)
LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[i]);
if(accordion_tab->hasFocus() && i>0)
{
+ bool prev_visible_tab_found = false;
while(i>0)
{
if(mAccordionTabs[--i]->getVisible())
+ {
+ prev_visible_tab_found = true;
break;
+ }
+ }
+
+ if (prev_visible_tab_found)
+ {
+ accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[i]);
+ accordion_tab->notify(LLSD().with("action","select_last"));
+ return 1;
}
-
- accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[i]);
- accordion_tab->notify(LLSD().with("action","select_last"));
- return 1;
+ break;
}
}
return 0;
diff --git a/indra/llui/llaccordionctrl.h b/indra/llui/llaccordionctrl.h
index 4cb0f38281..ab7d6548ca 100644
--- a/indra/llui/llaccordionctrl.h
+++ b/indra/llui/llaccordionctrl.h
@@ -81,12 +81,18 @@ public:
virtual BOOL handleRightMouseDown ( S32 x, S32 y, MASK mask);
virtual BOOL handleScrollWheel ( S32 x, S32 y, S32 clicks );
virtual BOOL handleKeyHere (KEY key, MASK mask);
+ virtual BOOL handleDragAndDrop (S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ std::string& tooltip_msg);
//
// Call reshape after changing splitter's size
virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
void addCollapsibleCtrl(LLView* view);
+ void removeCollapsibleCtrl(LLView* view);
void arrange();
@@ -100,6 +106,9 @@ public:
void reset ();
private:
+ void arrangeSinge();
+ void arrangeMultiple();
+
// Calc Splitter's height that is necessary to display all child content
S32 calcRecuiredHeight();
S32 getRecuiredHeight() const { return mInnerRect.getHeight(); }
@@ -112,11 +121,15 @@ private:
void showScrollbar (S32 width, S32 height);
void hideScrollbar (S32 width, S32 height);
+ BOOL autoScroll (S32 x, S32 y);
+
private:
LLRect mInnerRect;
LLScrollbar* mScrollbar;
bool mSingleExpansion;
bool mFitParent;
+ bool mAutoScrolling;
+ F32 mAutoScrollRate;
};
diff --git a/indra/llui/llaccordionctrltab.cpp b/indra/llui/llaccordionctrltab.cpp
index 4bfe44135a..3c706ce90e 100644
--- a/indra/llui/llaccordionctrltab.cpp
+++ b/indra/llui/llaccordionctrltab.cpp
@@ -33,8 +33,9 @@
#include "linden_common.h"
#include "lluictrl.h"
-
+#include "llscrollbar.h"
#include "llaccordionctrltab.h"
+#include "lllocalcliprect.h"
#include "lltextbox.h"
@@ -45,6 +46,9 @@ static const std::string DD_HEADER_NAME = "dd_header";
static const S32 HEADER_HEIGHT = 20;
static const S32 HEADER_IMAGE_LEFT_OFFSET = 5;
static const S32 HEADER_TEXT_LEFT_OFFSET = 30;
+static const F32 AUTO_OPEN_TIME = 1.f;
+static const S32 VERTICAL_MULTIPLE = 16;
+static const S32 PARENT_BORDER_MARGIN = 5;
static LLDefaultChildRegistry::Register<LLAccordionCtrlTab> t1("accordion_tab");
@@ -73,6 +77,11 @@ public:
virtual void onMouseEnter(S32 x, S32 y, MASK mask);
virtual void onMouseLeave(S32 x, S32 y, MASK mask);
virtual BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent);
+ virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ std::string& tooltip_msg);
private:
LLTextBox* mHeaderTextbox;
@@ -92,6 +101,8 @@ private:
LLUIColor mHeaderBGColor;
bool mNeedsHighlight;
+
+ LLFrameTimer mAutoOpenTimer;
};
LLAccordionCtrlTab::LLAccordionCtrlTabHeader::Params::Params()
@@ -209,6 +220,7 @@ void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::onMouseLeave(S32 x, S32 y, MA
{
LLUICtrl::onMouseLeave(x, y, mask);
mNeedsHighlight = false;
+ mAutoOpenTimer.stop();
}
BOOL LLAccordionCtrlTab::LLAccordionCtrlTabHeader::handleKey(KEY key, MASK mask, BOOL called_from_parent)
{
@@ -218,8 +230,33 @@ BOOL LLAccordionCtrlTab::LLAccordionCtrlTabHeader::handleKey(KEY key, MASK mask,
}
return LLUICtrl::handleKey(key, mask, called_from_parent);
}
+BOOL LLAccordionCtrlTab::LLAccordionCtrlTabHeader::handleDragAndDrop(S32 x, S32 y, MASK mask,
+ BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ std::string& tooltip_msg)
+{
+ LLAccordionCtrlTab* parent = dynamic_cast<LLAccordionCtrlTab*>(getParent());
+ if ( parent && !parent->getDisplayChildren() && parent->getCollapsible() && parent->canOpenClose() )
+ {
+ if (mAutoOpenTimer.getStarted())
+ {
+ if (mAutoOpenTimer.getElapsedTimeF32() > AUTO_OPEN_TIME)
+ {
+ parent->changeOpenClose(false);
+ mAutoOpenTimer.stop();
+ return TRUE;
+ }
+ }
+ else
+ mAutoOpenTimer.start();
+ }
+ return LLUICtrl::handleDragAndDrop(x, y, mask, drop, cargo_type,
+ cargo_data, accept, tooltip_msg);
+}
LLAccordionCtrlTab::Params::Params()
: title("title")
,display_children("expanded", true)
@@ -243,6 +280,7 @@ LLAccordionCtrlTab::Params::Params()
,header_image_pressed("header_image_pressed")
,header_image_focused("header_image_focused")
,header_text_color("header_text_color")
+ ,fit_panel("fit_panel",true)
{
mouse_opaque(false);
}
@@ -259,6 +297,9 @@ LLAccordionCtrlTab::LLAccordionCtrlTab(const LLAccordionCtrlTab::Params&p)
,mPaddingTop(p.padding_top)
,mPaddingBottom(p.padding_bottom)
,mCanOpenClose(true)
+ ,mFitPanel(p.fit_panel)
+ ,mContainerPanel(NULL)
+ ,mScrollbar(NULL)
{
mStoredOpenCloseState = false;
mWasStateStored = false;
@@ -287,54 +328,42 @@ void LLAccordionCtrlTab::setDisplayChildren(bool display)
mExpandedHeight : HEADER_HEIGHT);
setRect(rect);
- for(child_list_const_iter_t it = getChildList()->begin();
- getChildList()->end() != it; ++it)
- {
- LLView* child = *it;
- if(DD_HEADER_NAME == child->getName())
- continue;
+ if(mContainerPanel)
+ mContainerPanel->setVisible(getDisplayChildren());
- child->setVisible(getDisplayChildren());
+ if(mDisplayChildren)
+ {
+ adjustContainerPanel();
}
+ else
+ {
+ if(mScrollbar)
+ mScrollbar->setVisible(false);
+ }
+
}
void LLAccordionCtrlTab::reshape(S32 width, S32 height, BOOL called_from_parent /* = TRUE */)
{
LLRect headerRect;
- LLUICtrl::reshape(width, height, TRUE);
-
headerRect.setLeftTopAndSize(
0,height,width,HEADER_HEIGHT);
mHeader->setRect(headerRect);
mHeader->reshape(headerRect.getWidth(), headerRect.getHeight());
- for(child_list_const_iter_t it = getChildList()->begin();
- getChildList()->end() != it; ++it)
- {
- LLView* child = *it;
- if(DD_HEADER_NAME == child->getName())
- continue;
- if(!child->getVisible())
- continue;
-
- LLRect childRect = child->getRect();
- S32 childWidth = width - getPaddingLeft() - getPaddingRight();
- S32 childHeight = height - getHeaderHeight() - getPaddingTop() - getPaddingBottom();
+ if(!mDisplayChildren)
+ return;
- child->reshape(childWidth,childHeight);
-
- childRect.setLeftTopAndSize(
- getPaddingLeft(),
- childHeight + getPaddingBottom(),
- childWidth,
- childHeight);
+ LLRect childRect;
- child->setRect(childRect);
-
- break;//suppose that there is only one panel
- }
+ childRect.setLeftTopAndSize(
+ getPaddingLeft(),
+ height - getHeaderHeight() - getPaddingTop(),
+ width - getPaddingLeft() - getPaddingRight(),
+ height - getHeaderHeight() - getPaddingTop() - getPaddingBottom() );
+ adjustContainerPanel(childRect);
}
void LLAccordionCtrlTab::changeOpenClose(bool is_open)
@@ -396,6 +425,9 @@ bool LLAccordionCtrlTab::addChild(LLView* child, S32 tab_group)
setDisplayChildren(getDisplayChildren());
}
+ if (!mContainerPanel)
+ mContainerPanel = findContainerView();
+
return res;
}
@@ -405,7 +437,7 @@ void LLAccordionCtrlTab::setAccordionView(LLView* panel)
}
-LLView* LLAccordionCtrlTab::getAccordionView()
+LLView* LLAccordionCtrlTab::findContainerView()
{
for(child_list_const_iter_t it = getChildList()->begin();
getChildList()->end() != it; ++it)
@@ -436,10 +468,49 @@ void LLAccordionCtrlTab::setHeaderVisible(bool value)
reshape(getRect().getWidth(), getRect().getHeight(), FALSE);
};
-//vurtual
+//virtual
BOOL LLAccordionCtrlTab::postBuild()
{
- mHeader->setVisible(mHeaderVisible);
+ if(mHeader)
+ mHeader->setVisible(mHeaderVisible);
+
+ static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
+
+ LLRect scroll_rect;
+ scroll_rect.setOriginAndSize(
+ getRect().getWidth() - scrollbar_size,
+ 1,
+ scrollbar_size,
+ getRect().getHeight() - 1);
+
+ mContainerPanel = findContainerView();
+
+ if(!mFitPanel)
+ {
+ LLScrollbar::Params sbparams;
+ sbparams.name("scrollable vertical");
+ sbparams.rect(scroll_rect);
+ sbparams.orientation(LLScrollbar::VERTICAL);
+ sbparams.doc_size(getRect().getHeight());
+ sbparams.doc_pos(0);
+ sbparams.page_size(getRect().getHeight());
+ sbparams.step_size(VERTICAL_MULTIPLE);
+ sbparams.follows.flags(FOLLOWS_RIGHT | FOLLOWS_TOP | FOLLOWS_BOTTOM);
+ sbparams.change_callback(boost::bind(&LLAccordionCtrlTab::onScrollPosChangeCallback, this, _1, _2));
+
+
+ mScrollbar = LLUICtrlFactory::create<LLScrollbar> (sbparams);
+ LLView::addChild( mScrollbar );
+ mScrollbar->setFollowsRight();
+ mScrollbar->setFollowsTop();
+ mScrollbar->setFollowsBottom();
+
+ mScrollbar->setVisible(false);
+ }
+
+ if(mContainerPanel)
+ mContainerPanel->setVisible(mDisplayChildren);
+
return LLUICtrl::postBuild();
}
bool LLAccordionCtrlTab::notifyChildren (const LLSD& info)
@@ -483,7 +554,8 @@ S32 LLAccordionCtrlTab::notifyParent(const LLSD& info)
}
//LLAccordionCtrl should rearrange accodion tab if one of accordion change its size
- getParent()->notifyParent(info);
+ if (getParent()) // A parent may not be set if tabs are added dynamically.
+ getParent()->notifyParent(info);
return 1;
}
else if(str_action == "select_prev")
@@ -528,6 +600,12 @@ BOOL LLAccordionCtrlTab::handleKey(KEY key, MASK mask, BOOL called_from_parent)
if( !header->hasFocus() )
return LLUICtrl::handleKey(key, mask, called_from_parent);
+ if ( (key == KEY_RETURN )&& mask == MASK_NONE)
+ {
+ changeOpenClose(getDisplayChildren());
+ return TRUE;
+ }
+
if ( (key == KEY_ADD || key == KEY_RIGHT)&& mask == MASK_NONE)
{
if(getDisplayChildren() == false)
@@ -588,6 +666,7 @@ void LLAccordionCtrlTab::storeOpenCloseState()
mStoredOpenCloseState = getDisplayChildren();
mWasStateStored = true;
}
+
void LLAccordionCtrlTab::restoreOpenCloseState()
{
if(!mWasStateStored)
@@ -598,3 +677,203 @@ void LLAccordionCtrlTab::restoreOpenCloseState()
}
mWasStateStored = false;
}
+
+void LLAccordionCtrlTab::adjustContainerPanel ()
+{
+ S32 width = getRect().getWidth();
+ S32 height = getRect().getHeight();
+
+ LLRect child_rect;
+ child_rect.setLeftTopAndSize(
+ getPaddingLeft(),
+ height - getHeaderHeight() - getPaddingTop(),
+ width - getPaddingLeft() - getPaddingRight(),
+ height - getHeaderHeight() - getPaddingTop() - getPaddingBottom() );
+
+ adjustContainerPanel(child_rect);
+}
+
+void LLAccordionCtrlTab::adjustContainerPanel(const LLRect& child_rect)
+{
+ if(!mContainerPanel)
+ return;
+
+ if(!mFitPanel)
+ {
+ show_hide_scrollbar(child_rect);
+ updateLayout(child_rect);
+ }
+ else
+ {
+ mContainerPanel->reshape(child_rect.getWidth(),child_rect.getHeight());
+ mContainerPanel->setRect(child_rect);
+ }
+}
+
+S32 LLAccordionCtrlTab::getChildViewHeight()
+{
+ if(!mContainerPanel)
+ return 0;
+ return mContainerPanel->getRect().getHeight();
+}
+
+void LLAccordionCtrlTab::show_hide_scrollbar(const LLRect& child_rect)
+{
+ if(getChildViewHeight() > child_rect.getHeight() )
+ showScrollbar(child_rect);
+ else
+ hideScrollbar(child_rect);
+}
+void LLAccordionCtrlTab::showScrollbar(const LLRect& child_rect)
+{
+ if(!mContainerPanel || !mScrollbar)
+ return;
+ bool was_visible = mScrollbar->getVisible();
+ mScrollbar->setVisible(true);
+
+ static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
+
+ {
+ ctrlSetLeftTopAndSize(mScrollbar,child_rect.getWidth()-scrollbar_size,
+ child_rect.getHeight()-PARENT_BORDER_MARGIN,
+ scrollbar_size,
+ child_rect.getHeight()-2*PARENT_BORDER_MARGIN);
+ }
+
+ LLRect orig_rect = mContainerPanel->getRect();
+
+ mScrollbar->setPageSize(child_rect.getHeight());
+ mScrollbar->setDocParams(orig_rect.getHeight(),mScrollbar->getDocPos());
+
+ if(was_visible)
+ {
+ S32 scroll_pos = llmin(mScrollbar->getDocPos(), orig_rect.getHeight() - child_rect.getHeight() - 1);
+ mScrollbar->setDocPos(scroll_pos);
+ }
+ else//shrink child panel
+ {
+ updateLayout(child_rect);
+ }
+
+}
+
+void LLAccordionCtrlTab::hideScrollbar( const LLRect& child_rect )
+{
+ if(!mContainerPanel || !mScrollbar)
+ return;
+
+ if(mScrollbar->getVisible() == false)
+ return;
+ mScrollbar->setVisible(false);
+ mScrollbar->setDocPos(0);
+
+ //shrink child panel
+ updateLayout(child_rect);
+}
+
+void LLAccordionCtrlTab::onScrollPosChangeCallback(S32, LLScrollbar*)
+{
+ LLRect child_rect;
+
+ S32 width = getRect().getWidth();
+ S32 height = getRect().getHeight();
+
+ child_rect.setLeftTopAndSize(
+ getPaddingLeft(),
+ height - getHeaderHeight() - getPaddingTop(),
+ width - getPaddingLeft() - getPaddingRight(),
+ height - getHeaderHeight() - getPaddingTop() - getPaddingBottom() );
+
+ updateLayout(child_rect);
+}
+
+void LLAccordionCtrlTab::drawChild(const LLRect& root_rect,LLView* child)
+{
+ if (child && child->getVisible() && child->getRect().isValid())
+ {
+ LLRect screen_rect;
+ localRectToScreen(child->getRect(),&screen_rect);
+
+ if ( root_rect.overlaps(screen_rect) && LLUI::sDirtyRect.overlaps(screen_rect))
+ {
+ glMatrixMode(GL_MODELVIEW);
+ LLUI::pushMatrix();
+ {
+ LLUI::translate((F32)child->getRect().mLeft, (F32)child->getRect().mBottom, 0.f);
+ child->draw();
+
+ }
+ LLUI::popMatrix();
+ }
+ }
+}
+
+void LLAccordionCtrlTab::draw()
+{
+ if(mFitPanel)
+ LLUICtrl::draw();
+ else
+ {
+ LLRect root_rect = getRootView()->getRect();
+ drawChild(root_rect,mHeader);
+ drawChild(root_rect,mScrollbar );
+ {
+ LLRect child_rect;
+
+ S32 width = getRect().getWidth();
+ S32 height = getRect().getHeight();
+
+ child_rect.setLeftTopAndSize(
+ getPaddingLeft(),
+ height - getHeaderHeight() - getPaddingTop(),
+ width - getPaddingLeft() - getPaddingRight(),
+ height - getHeaderHeight() - getPaddingTop() - getPaddingBottom() );
+
+ LLLocalClipRect clip(child_rect);
+ drawChild(root_rect,mContainerPanel);
+ }
+ }
+}
+
+void LLAccordionCtrlTab::updateLayout ( const LLRect& child_rect )
+{
+ LLView* child = getAccordionView();
+ if(!mContainerPanel)
+ return;
+
+ S32 panel_top = child_rect.getHeight();
+ S32 panel_width = child_rect.getWidth();
+
+ static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
+ if(mScrollbar->getVisible() != false)
+ {
+ panel_top+=mScrollbar->getDocPos();
+ panel_width-=scrollbar_size;
+ }
+
+ //set sizes for first panels and dragbars
+ LLRect panel_rect = child->getRect();
+ ctrlSetLeftTopAndSize(mContainerPanel,child_rect.mLeft,panel_top,panel_width,panel_rect.getHeight());
+}
+void LLAccordionCtrlTab::ctrlSetLeftTopAndSize(LLView* panel, S32 left, S32 top, S32 width, S32 height)
+{
+ if(!panel)
+ return;
+ LLRect panel_rect = panel->getRect();
+ panel_rect.setLeftTopAndSize( left, top, width, height);
+ panel->reshape( width, height, 1);
+ panel->setRect(panel_rect);
+}
+BOOL LLAccordionCtrlTab::handleToolTip(S32 x, S32 y, MASK mask)
+{
+ //header may be not the first child but we need to process it first
+ if(y >= (getRect().getHeight() - HEADER_HEIGHT - HEADER_HEIGHT/2) )
+ {
+ //inside tab header
+ //fix for EXT-6619
+ return TRUE;
+ }
+ return LLUICtrl::handleToolTip(x, y, mask);
+}
+
+
diff --git a/indra/llui/llaccordionctrltab.h b/indra/llui/llaccordionctrltab.h
index b200d43438..fb19d17e99 100644
--- a/indra/llui/llaccordionctrltab.h
+++ b/indra/llui/llaccordionctrltab.h
@@ -35,12 +35,14 @@
#include <string>
#include "llrect.h"
+#include "lluictrl.h"
+#include "lluicolor.h"
-class LLUICtrl;
class LLUICtrlFactory;
class LLUIImage;
class LLButton;
class LLTextBox;
+class LLScrollbar;
@@ -84,6 +86,8 @@ public:
Optional<bool> header_visible;
+ Optional<bool> fit_panel;
+
Optional<S32> padding_left;
Optional<S32> padding_right;
Optional<S32> padding_top;
@@ -107,7 +111,7 @@ public:
//set LLAccordionCtrlTab panel
void setAccordionView(LLView* panel);
- LLView* getAccordionView();
+ LLView* getAccordionView() { return mContainerPanel; };
bool getCollapsible() {return mCollapsible;};
@@ -115,6 +119,7 @@ public:
void changeOpenClose(bool is_open);
void canOpenClose(bool can_open_close) { mCanOpenClose = can_open_close;};
+ bool canOpenClose() const { return mCanOpenClose; };
virtual BOOL postBuild();
@@ -122,6 +127,8 @@ public:
S32 notify(const LLSD& info);
bool notifyChildren(const LLSD& info);
+ void draw();
+
void storeOpenCloseState ();
void restoreOpenCloseState ();
@@ -141,6 +148,8 @@ public:
virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
virtual BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent);
+ virtual BOOL handleToolTip(S32 x, S32 y, MASK mask);
+
virtual bool addChild(LLView* child, S32 tab_group);
bool isExpanded() { return mDisplayChildren; }
@@ -163,9 +172,26 @@ public:
void showAndFocusHeader();
-private:
+ void setFitPanel( bool fit ) { mFitPanel = true; }
-
+protected:
+ void adjustContainerPanel (const LLRect& child_rect);
+ void adjustContainerPanel ();
+ S32 getChildViewHeight ();
+
+ void onScrollPosChangeCallback(S32, LLScrollbar*);
+
+ void show_hide_scrollbar (const LLRect& child_rect);
+ void showScrollbar (const LLRect& child_rect);
+ void hideScrollbar (const LLRect& child_rect);
+
+ void updateLayout ( const LLRect& child_rect );
+ void ctrlSetLeftTopAndSize (LLView* panel, S32 left, S32 top, S32 width, S32 height);
+
+ void drawChild(const LLRect& root_rect,LLView* child);
+
+ LLView* findContainerView ();
+private:
class LLAccordionCtrlTabHeader;
LLAccordionCtrlTabHeader* mHeader; //Header
@@ -175,6 +201,7 @@ private:
bool mHeaderVisible;
bool mCanOpenClose;
+ bool mFitPanel;
S32 mPaddingLeft;
S32 mPaddingRight;
@@ -184,6 +211,8 @@ private:
bool mStoredOpenCloseState;
bool mWasStateStored;
+ LLScrollbar* mScrollbar;
+ LLView* mContainerPanel;
LLUIColor mDropdownBGColor;
};
diff --git a/indra/llui/llbutton.cpp b/indra/llui/llbutton.cpp
index e9f6288f44..0255061b12 100644
--- a/indra/llui/llbutton.cpp
+++ b/indra/llui/llbutton.cpp
@@ -81,6 +81,9 @@ LLButton::Params::Params()
image_pressed_selected("image_pressed_selected"),
image_overlay("image_overlay"),
image_overlay_alignment("image_overlay_alignment", std::string("center")),
+ image_top_pad("image_top_pad"),
+ image_bottom_pad("image_bottom_pad"),
+ imgoverlay_label_space("imgoverlay_label_space", 1),
label_color("label_color"),
label_color_selected("label_color_selected"), // requires is_toggle true
label_color_disabled("label_color_disabled"),
@@ -140,6 +143,9 @@ LLButton::LLButton(const LLButton::Params& p)
mImageOverlay(p.image_overlay()),
mImageOverlayColor(p.image_overlay_color()),
mImageOverlayAlignment(LLFontGL::hAlignFromName(p.image_overlay_alignment)),
+ mImageOverlayTopPad(p.image_top_pad),
+ mImageOverlayBottomPad(p.image_bottom_pad),
+ mImgOverlayLabelSpace(p.imgoverlay_label_space),
mIsToggle(p.is_toggle),
mScaleImage(p.scale_image),
mDropShadowedText(p.label_shadow),
@@ -763,6 +769,7 @@ void LLButton::draw()
center_x++;
}
+ center_y += (mImageOverlayBottomPad - mImageOverlayTopPad);
// fade out overlay images on disabled buttons
LLColor4 overlay_color = mImageOverlayColor.get();
if (!enabled)
@@ -774,10 +781,9 @@ void LLButton::draw()
switch(mImageOverlayAlignment)
{
case LLFontGL::LEFT:
- text_left += overlay_width + 1;
- text_width -= overlay_width + 1;
+ text_left += overlay_width + mImgOverlayLabelSpace;
mImageOverlay->draw(
- mLeftHPad,
+ mLeftHPad,
center_y - (overlay_height / 2),
overlay_width,
overlay_height,
@@ -792,10 +798,9 @@ void LLButton::draw()
overlay_color);
break;
case LLFontGL::RIGHT:
- text_right -= overlay_width + 1;
- text_width -= overlay_width + 1;
+ text_right -= overlay_width + mImgOverlayLabelSpace;
mImageOverlay->draw(
- getRect().getWidth() - mRightHPad - overlay_width,
+ getRect().getWidth() - mRightHPad - overlay_width,
center_y - (overlay_height / 2),
overlay_width,
overlay_height,
@@ -819,7 +824,7 @@ void LLButton::draw()
x = text_right;
break;
case LLFontGL::HCENTER:
- x = getRect().getWidth() / 2;
+ x = text_left + (text_width / 2);
break;
case LLFontGL::LEFT:
default:
@@ -998,6 +1003,11 @@ void LLButton::setImageDisabledSelected(LLPointer<LLUIImage> image)
mFadeWhenDisabled = TRUE;
}
+void LLButton::setImagePressed(LLPointer<LLUIImage> image)
+{
+ mImagePressed = image;
+}
+
void LLButton::setImageHoverSelected(LLPointer<LLUIImage> image)
{
mImageHoverSelected = image;
@@ -1022,6 +1032,20 @@ void LLButton::setImageOverlay(const std::string& image_name, LLFontGL::HAlign a
}
}
+void LLButton::setImageOverlay(const LLUUID& image_id, LLFontGL::HAlign alignment, const LLColor4& color)
+{
+ if (image_id.isNull())
+ {
+ mImageOverlay = NULL;
+ }
+ else
+ {
+ mImageOverlay = LLUI::getUIImageByID(image_id);
+ mImageOverlayAlignment = alignment;
+ mImageOverlayColor = color;
+ }
+}
+
void LLButton::onMouseCaptureLost()
{
resetMouseDownTimer();
diff --git a/indra/llui/llbutton.h b/indra/llui/llbutton.h
index 5e28b8cdff..a4d81ed6c3 100644
--- a/indra/llui/llbutton.h
+++ b/indra/llui/llbutton.h
@@ -106,6 +106,15 @@ public:
Optional<S32> pad_left;
Optional<S32> pad_bottom; // under text label
+ //image overlay paddings
+ Optional<S32> image_top_pad;
+ Optional<S32> image_bottom_pad;
+
+ /**
+ * Space between image_overlay and label
+ */
+ Optional<S32> imgoverlay_label_space;
+
// callbacks
Optional<CommitCallbackParam> click_callback, // alias -> commit_callback
mouse_down_callback,
@@ -186,6 +195,11 @@ public:
void setLeftHPad( S32 pad ) { mLeftHPad = pad; }
void setRightHPad( S32 pad ) { mRightHPad = pad; }
+ void setImageOverlayTopPad( S32 pad ) { mImageOverlayTopPad = pad; }
+ S32 getImageOverlayTopPad() const { return mImageOverlayTopPad; }
+ void setImageOverlayBottomPad( S32 pad ) { mImageOverlayBottomPad = pad; }
+ S32 getImageOverlayBottomPad() const { return mImageOverlayBottomPad; }
+
const std::string getLabelUnselected() const { return wstring_to_utf8str(mUnselectedLabel); }
const std::string getLabelSelected() const { return wstring_to_utf8str(mSelectedLabel); }
@@ -200,8 +214,10 @@ public:
void setDisabledSelectedLabelColor( const LLColor4& c ) { mDisabledSelectedLabelColor = c; }
void setImageOverlay(const std::string& image_name, LLFontGL::HAlign alignment = LLFontGL::HCENTER, const LLColor4& color = LLColor4::white);
+ void setImageOverlay(const LLUUID& image_id, LLFontGL::HAlign alignment = LLFontGL::HCENTER, const LLColor4& color = LLColor4::white);
LLPointer<LLUIImage> getImageOverlay() { return mImageOverlay; }
-
+ LLFontGL::HAlign getImageOverlayHAlign() const { return mImageOverlayAlignment; }
+
void autoResize(); // resize with label of current btn state
void resize(LLUIString label); // resize with label input
void setLabel( const LLStringExplicit& label);
@@ -230,6 +246,7 @@ public:
void setImageHoverUnselected(LLPointer<LLUIImage> image);
void setImageDisabled(LLPointer<LLUIImage> image);
void setImageDisabledSelected(LLPointer<LLUIImage> image);
+ void setImagePressed(LLPointer<LLUIImage> image);
void setCommitOnReturn(BOOL commit) { mCommitOnReturn = commit; }
BOOL getCommitOnReturn() const { return mCommitOnReturn; }
@@ -242,6 +259,8 @@ public:
void setForcePressedState(bool b) { mForcePressedState = b; }
+ void setAutoResize(bool auto_resize) { mAutoResize = auto_resize; }
+
protected:
LLPointer<LLUIImage> getImageUnselected() const { return mImageUnselected; }
LLPointer<LLUIImage> getImageSelected() const { return mImageSelected; }
@@ -312,6 +331,14 @@ private:
S32 mRightHPad;
S32 mBottomVPad; // under text label
+ S32 mImageOverlayTopPad;
+ S32 mImageOverlayBottomPad;
+
+ /*
+ * Space between image_overlay and label
+ */
+ S32 mImgOverlayLabelSpace;
+
F32 mHoverGlowStrength;
F32 mCurGlowStrength;
diff --git a/indra/llui/llcombobox.cpp b/indra/llui/llcombobox.cpp
index f29e8785eb..cc107c972d 100644
--- a/indra/llui/llcombobox.cpp
+++ b/indra/llui/llcombobox.cpp
@@ -103,7 +103,8 @@ LLComboBox::LLComboBox(const LLComboBox::Params& p)
mPrearrangeCallback(p.prearrange_callback()),
mTextEntryCallback(p.text_entry_callback()),
mListPosition(p.list_position),
- mLastSelectedIndex(-1)
+ mLastSelectedIndex(-1),
+ mLabel(p.label)
{
// Text label button
@@ -159,7 +160,7 @@ LLComboBox::LLComboBox(const LLComboBox::Params& p)
createLineEditor(p);
- setTopLostCallback(boost::bind(&LLComboBox::hideList, this));
+ mTopLostSignalConnection = setTopLostCallback(boost::bind(&LLComboBox::hideList, this));
}
void LLComboBox::initFromParams(const LLComboBox::Params& p)
@@ -186,6 +187,9 @@ BOOL LLComboBox::postBuild()
LLComboBox::~LLComboBox()
{
// children automatically deleted, including mMenu, mButton
+
+ // explicitly disconect this signal, since base class destructor might fire top lost
+ mTopLostSignalConnection.disconnect();
}
@@ -319,15 +323,19 @@ void LLComboBox::setValue(const LLSD& value)
LLScrollListItem* item = mList->getFirstSelected();
if (item)
{
- setLabel( mList->getSelectedItemLabel() );
+ setLabel(getSelectedItemLabel());
}
mLastSelectedIndex = mList->getFirstSelectedIndex();
}
+ else
+ {
+ mLastSelectedIndex = -1;
+ }
}
const std::string LLComboBox::getSimple() const
{
- const std::string res = mList->getSelectedItemLabel();
+ const std::string res = getSelectedItemLabel();
if (res.empty() && mAllowTextEntry)
{
return mTextEntry->getText();
@@ -406,7 +414,7 @@ BOOL LLComboBox::remove(S32 index)
if (index < mList->getItemCount())
{
mList->deleteSingleItem(index);
- setLabel(mList->getSelectedItemLabel());
+ setLabel(getSelectedItemLabel());
return TRUE;
}
return FALSE;
@@ -447,7 +455,7 @@ BOOL LLComboBox::setCurrentByIndex( S32 index )
BOOL found = mList->selectNthItem( index );
if (found)
{
- setLabel(mList->getSelectedItemLabel());
+ setLabel(getSelectedItemLabel());
mLastSelectedIndex = index;
}
return found;
@@ -487,9 +495,9 @@ void LLComboBox::createLineEditor(const LLComboBox::Params& p)
params.max_length_bytes(mMaxChars);
params.commit_callback.function(boost::bind(&LLComboBox::onTextCommit, this, _2));
params.keystroke_callback(boost::bind(&LLComboBox::onTextEntry, this, _1));
- params.handle_edit_keys_directly(true);
params.commit_on_focus_lost(false);
params.follows.flags(FOLLOWS_ALL);
+ params.label(mLabel);
mTextEntry = LLUICtrlFactory::create<LLLineEditor> (params);
mTextEntry->setText(cur_label);
mTextEntry->setIgnoreTab(TRUE);
@@ -505,7 +513,8 @@ void LLComboBox::createLineEditor(const LLComboBox::Params& p)
mButton->setRect(rect);
mButton->setTabStop(TRUE);
mButton->setHAlign(LLFontGL::LEFT);
-
+ mButton->setLabel(mLabel.getString());
+
if (mTextEntry)
{
mTextEntry->setVisible(FALSE);
@@ -609,16 +618,14 @@ void LLComboBox::showList()
mList->setFocus(TRUE);
- // register ourselves as a "top" control
- // effectively putting us into a special draw layer
- // and not affecting the bounding rectangle calculation
- gFocusMgr.setTopCtrl(this);
-
// Show the list and push the button down
mButton->setToggleState(TRUE);
mList->setVisible(TRUE);
+ LLUI::addPopup(this);
+
setUseBoundingRect(TRUE);
+// updateBoundingRect();
}
void LLComboBox::hideList()
@@ -633,7 +640,7 @@ void LLComboBox::hideList()
if(mLastSelectedIndex >= 0)
mList->selectNthItem(mLastSelectedIndex);
}
- else
+ else if(mLastSelectedIndex >= 0)
mList->selectNthItem(mLastSelectedIndex);
mButton->setToggleState(FALSE);
@@ -641,10 +648,8 @@ void LLComboBox::hideList()
mList->mouseOverHighlightNthItem(-1);
setUseBoundingRect(FALSE);
- if( gFocusMgr.getTopCtrl() == this )
- {
- gFocusMgr.setTopCtrl(NULL);
- }
+ LLUI::removePopup(this);
+// updateBoundingRect();
}
}
@@ -700,13 +705,10 @@ void LLComboBox::onListMouseUp()
void LLComboBox::onItemSelected(const LLSD& data)
{
- const std::string name = mList->getSelectedItemLabel();
-
- S32 cur_id = getCurrentIndex();
- mLastSelectedIndex = cur_id;
- if (cur_id != -1)
+ mLastSelectedIndex = getCurrentIndex();
+ if (mLastSelectedIndex != -1)
{
- setLabel(name);
+ setLabel(getSelectedItemLabel());
if (mAllowTextEntry)
{
@@ -714,7 +716,6 @@ void LLComboBox::onItemSelected(const LLSD& data)
mTextEntry->selectAll();
}
}
-
// hiding the list reasserts the old value stored in the text editor/dropdown button
hideList();
@@ -909,7 +910,7 @@ void LLComboBox::updateSelection()
}
else if (mList->selectItemByPrefix(left_wstring, FALSE))
{
- LLWString selected_item = utf8str_to_wstring(mList->getSelectedItemLabel());
+ LLWString selected_item = utf8str_to_wstring(getSelectedItemLabel());
LLWString wtext = left_wstring + selected_item.substr(left_wstring.size(), selected_item.size());
mTextEntry->setText(wstring_to_utf8str(wtext));
mTextEntry->setSelection(left_wstring.size(), mTextEntry->getWText().size());
@@ -1011,7 +1012,7 @@ BOOL LLComboBox::setCurrentByID(const LLUUID& id)
if (found)
{
- setLabel(mList->getSelectedItemLabel());
+ setLabel(getSelectedItemLabel());
mLastSelectedIndex = mList->getFirstSelectedIndex();
}
@@ -1027,7 +1028,7 @@ BOOL LLComboBox::setSelectedByValue(const LLSD& value, BOOL selected)
BOOL found = mList->setSelectedByValue(value, selected);
if (found)
{
- setLabel(mList->getSelectedItemLabel());
+ setLabel(getSelectedItemLabel());
}
return found;
}
@@ -1066,3 +1067,24 @@ BOOL LLComboBox::selectItemRange( S32 first, S32 last )
{
return mList->selectItemRange(first, last);
}
+
+
+static LLDefaultChildRegistry::Register<LLIconsComboBox> register_icons_combo_box("icons_combo_box");
+
+LLIconsComboBox::Params::Params()
+: icon_column("icon_column", ICON_COLUMN),
+ label_column("label_column", LABEL_COLUMN)
+{}
+
+LLIconsComboBox::LLIconsComboBox(const LLIconsComboBox::Params& p)
+: LLComboBox(p),
+ mIconColumnIndex(p.icon_column),
+ mLabelColumnIndex(p.label_column)
+{}
+
+const std::string LLIconsComboBox::getSelectedItemLabel(S32 column) const
+{
+ mButton->setImageOverlay(LLComboBox::getSelectedItemLabel(mIconColumnIndex), mButton->getImageOverlayHAlign());
+
+ return LLComboBox::getSelectedItemLabel(mLabelColumnIndex);
+}
diff --git a/indra/llui/llcombobox.h b/indra/llui/llcombobox.h
index 4f27588467..f0bd432f3a 100644
--- a/indra/llui/llcombobox.h
+++ b/indra/llui/llcombobox.h
@@ -150,7 +150,7 @@ public:
// Get name of current item. Returns an empty string if not found.
const std::string getSimple() const;
// Get contents of column x of selected row
- const std::string getSelectedItemLabel(S32 column = 0) const;
+ virtual const std::string getSelectedItemLabel(S32 column = 0) const;
// Sets the label, which doesn't have to exist in the label.
// This is probably a UI abuse.
@@ -230,6 +230,38 @@ private:
commit_callback_t mPrearrangeCallback;
commit_callback_t mTextEntryCallback;
commit_callback_t mSelectionCallback;
- S32 mLastSelectedIndex;
+ boost::signals2::connection mTopLostSignalConnection;
+ S32 mLastSelectedIndex;
};
+
+// A combo box with icons for the list of items.
+class LLIconsComboBox
+: public LLComboBox
+{
+public:
+ struct Params
+ : public LLInitParam::Block<Params, LLComboBox::Params>
+ {
+ Optional<S32> icon_column,
+ label_column;
+ Params();
+ };
+
+ /*virtual*/ const std::string getSelectedItemLabel(S32 column = 0) const;
+
+private:
+ enum EColumnIndex
+ {
+ ICON_COLUMN = 0,
+ LABEL_COLUMN
+ };
+
+ friend class LLUICtrlFactory;
+ LLIconsComboBox(const Params&);
+ virtual ~LLIconsComboBox() {};
+
+ S32 mIconColumnIndex;
+ S32 mLabelColumnIndex;
+};
+
#endif
diff --git a/indra/llui/llconsole.cpp b/indra/llui/llconsole.cpp
index 59499f987b..badbddc3cc 100644
--- a/indra/llui/llconsole.cpp
+++ b/indra/llui/llconsole.cpp
@@ -66,7 +66,9 @@ LLConsole::LLConsole(const LLConsole::Params& p)
: LLUICtrl(p),
LLFixedBuffer(p.max_lines),
mLinePersistTime(p.persist_time), // seconds
- mFont(p.font)
+ mFont(p.font),
+ mConsoleWidth(0),
+ mConsoleHeight(0)
{
if (p.font_size_index.isProvided())
{
@@ -242,23 +244,6 @@ void LLConsole::draw()
}
}
-void LLConsole::addLine(const std::string& utf8line)
-{
- LLWString wline = utf8str_to_wstring(utf8line);
- addLine(wline, 0.f, LLColor4(1.f, 1.f, 1.f, 1.f));
-}
-
-void LLConsole::addLine(const LLWString& wline)
-{
- addLine(wline, 0.f, LLColor4(1.f, 1.f, 1.f, 1.f));
-}
-
-void LLConsole::addLine(const std::string& utf8line, F32 size, const LLColor4 &color)
-{
- LLWString wline = utf8str_to_wstring(utf8line);
- addLine(wline, size, color);
-}
-
//Generate highlight color segments for this paragraph. Pass in default color of paragraph.
void LLConsole::Paragraph::makeParagraphColorSegments (const LLColor4 &color)
{
@@ -315,7 +300,8 @@ void LLConsole::Paragraph::updateLines(F32 screen_width, const LLFontGL* font, b
S32 paragraph_offset = 0; //Offset into the paragraph text.
// Wrap lines that are longer than the view is wide.
- while( paragraph_offset < (S32)mParagraphText.length() )
+ while( paragraph_offset < (S32)mParagraphText.length() &&
+ mParagraphText[paragraph_offset] != 0)
{
S32 skip_chars; // skip '\n'
// Figure out if a word-wrapped line fits here.
@@ -381,21 +367,45 @@ void LLConsole::Paragraph::updateLines(F32 screen_width, const LLFontGL* font, b
//Pass in the string and the default color for this block of text.
LLConsole::Paragraph::Paragraph (LLWString str, const LLColor4 &color, F32 add_time, const LLFontGL* font, F32 screen_width)
- : mParagraphText(str), mAddTime(add_time), mMaxWidth(-1)
+: mParagraphText(str), mAddTime(add_time), mMaxWidth(-1)
{
makeParagraphColorSegments(color);
updateLines( screen_width, font );
}
-void LLConsole::addLine(const LLWString& wline, F32 size, const LLColor4 &color)
+// called once per frame regardless of console visibility
+// static
+void LLConsole::updateClass()
{
- Paragraph paragraph(wline, color, mTimer.getElapsedTimeF32(), mFont, (F32)getRect().getWidth() );
-
- mParagraphs.push_back ( paragraph );
+ LLInstanceTrackerScopedGuard guard;
+
+ for (instance_iter it = guard.beginInstances(); it != guard.endInstances(); ++it)
+ {
+ it->update();
+ }
+}
+
+void LLConsole::update()
+{
+ {
+ LLMutexLock lock(&mMutex);
+
+ while (!mLines.empty())
+ {
+ mParagraphs.push_back(
+ Paragraph( mLines.front(),
+ LLColor4::white,
+ mTimer.getElapsedTimeF32(),
+ mFont,
+ (F32)getRect().getWidth()));
+ mLines.pop_front();
+ }
+ }
// remove old paragraphs which can't possibly be visible any more. ::draw() will do something similar but more conservative - we do this here because ::draw() isn't guaranteed to ever be called! (i.e. the console isn't visible)
- while ((S32)mParagraphs.size() > llmax((S32)0, (S32)(mMaxLines)))
- {
- mParagraphs.pop_front();
- }
+ while ((S32)mParagraphs.size() > llmax((S32)0, (S32)(mMaxLines)))
+ {
+ mParagraphs.pop_front();
+ }
}
+
diff --git a/indra/llui/llconsole.h b/indra/llui/llconsole.h
index 5800a82922..f38e2bc9c2 100644
--- a/indra/llui/llconsole.h
+++ b/indra/llui/llconsole.h
@@ -40,7 +40,7 @@
class LLSD;
-class LLConsole : public LLFixedBuffer, public LLUICtrl
+class LLConsole : public LLFixedBuffer, public LLUICtrl, public LLInstanceTracker<LLConsole>
{
public:
typedef enum e_font_size
@@ -68,6 +68,9 @@ protected:
friend class LLUICtrlFactory;
public:
+ // call once per frame to pull data out of LLFixedBuffer
+ static void updateClass();
+
//A paragraph color segment defines the color of text in a line
//of text that was received for console display. It has no
//notion of line wraps, screen position, or the text it contains.
@@ -139,19 +142,15 @@ public:
// -1 = monospace, 0 means small, font size = 1 means big
void setFontSize(S32 size_index);
- void addLine(const std::string& utf8line, F32 size, const LLColor4 &color);
- void addLine(const LLWString& wline, F32 size, const LLColor4 &color);
// Overrides
/*virtual*/ void draw();
- /*virtual*/ void addLine(const std::string& utf8line);
- /*virtual*/ void addLine(const LLWString& line);
private:
+ void update();
+
F32 mLinePersistTime; // Age at which to stop drawing.
F32 mFadeTime; // Age at which to start fading
const LLFontGL* mFont;
- S32 mLastBoxHeight;
- S32 mLastBoxWidth;
S32 mConsoleWidth;
S32 mConsoleHeight;
diff --git a/indra/llui/lldockablefloater.cpp b/indra/llui/lldockablefloater.cpp
index 74438b184a..3d8670fef2 100644
--- a/indra/llui/lldockablefloater.cpp
+++ b/indra/llui/lldockablefloater.cpp
@@ -95,7 +95,7 @@ void LLDockableFloater::toggleInstance(const LLSD& sdname)
LLDockableFloater* instance =
dynamic_cast<LLDockableFloater*> (LLFloaterReg::findInstance(name));
// if floater closed or docked
- if (instance == NULL || instance != NULL && instance->isDocked())
+ if (instance == NULL || (instance && instance->isDocked()))
{
LLFloaterReg::toggleInstance(name, key);
// restore button toggle state
@@ -146,7 +146,7 @@ void LLDockableFloater::setVisible(BOOL visible)
if (visible)
{
- LLFloater::setFrontmost(TRUE);
+ LLFloater::setFrontmost(getAutoFocus());
}
LLFloater::setVisible(visible);
}
@@ -202,10 +202,6 @@ void LLDockableFloater::setDocked(bool docked, bool pop_on_undock)
translate(0, UNDOCK_LEAP_HEIGHT);
}
}
- else
- {
- docked = false;
- }
LLFloater::setDocked(docked, pop_on_undock);
}
@@ -226,7 +222,7 @@ void LLDockableFloater::draw()
void LLDockableFloater::setDockControl(LLDockControl* dockControl)
{
mDockControl.reset(dockControl);
- setDocked(mDockControl.get() != NULL && mDockControl.get()->isDockVisible());
+ setDocked(isDocked());
}
const LLUIImagePtr& LLDockableFloater::getDockTongue()
diff --git a/indra/llui/lldockcontrol.cpp b/indra/llui/lldockcontrol.cpp
index 0d8e54aa48..d738b10130 100644
--- a/indra/llui/lldockcontrol.cpp
+++ b/indra/llui/lldockcontrol.cpp
@@ -37,7 +37,11 @@
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)
+ mDockWidget(dockWidget),
+ mDockableFloater(dockableFloater),
+ mDockTongue(dockTongue),
+ mDockTongueX(0),
+ mDockTongueY(0)
{
mDockAt = dockAt;
@@ -158,7 +162,9 @@ bool LLDockControl::isDockVisible()
{
case LEFT: // to keep compiler happy
break;
+ case BOTTOM:
case TOP:
+ {
// check is dock inside parent rect
LLRect dockParentRect =
mDockWidget->getParent()->calcScreenRect();
@@ -169,6 +175,9 @@ bool LLDockControl::isDockVisible()
}
break;
}
+ default:
+ break;
+ }
}
}
@@ -251,6 +260,42 @@ void LLDockControl::moveDockable()
mDockTongueY = dockRect.mTop;
break;
+ case BOTTOM:
+ x = dockRect.getCenterX() - dockableRect.getWidth() / 2;
+ y = dockRect.mBottom;
+ // unique docking used with dock tongue, so add tongue height o the Y coordinate
+ if (use_tongue)
+ {
+ y -= mDockTongue->getHeight();
+ }
+
+ // check is dockable inside root view rect
+ if (x < rootRect.mLeft)
+ {
+ x = rootRect.mLeft;
+ }
+ if (x + dockableRect.getWidth() > rootRect.mRight)
+ {
+ x = rootRect.mRight - dockableRect.getWidth();
+ }
+
+ // calculate dock tongue position
+ dockParentRect = mDockWidget->getParent()->calcScreenRect();
+ if (dockRect.getCenterX() < dockParentRect.mLeft)
+ {
+ mDockTongueX = dockParentRect.mLeft - mDockTongue->getWidth() / 2;
+ }
+ else if (dockRect.getCenterX() > dockParentRect.mRight)
+ {
+ mDockTongueX = dockParentRect.mRight - mDockTongue->getWidth() / 2;;
+ }
+ else
+ {
+ mDockTongueX = dockRect.getCenterX() - mDockTongue->getWidth() / 2;
+ }
+ mDockTongueY = dockRect.mBottom - mDockTongue->getHeight();
+
+ break;
}
// move dockable
diff --git a/indra/llui/lldockcontrol.h b/indra/llui/lldockcontrol.h
index 550955c4c5..a5caf68001 100644
--- a/indra/llui/lldockcontrol.h
+++ b/indra/llui/lldockcontrol.h
@@ -47,8 +47,9 @@ class LLDockControl
public:
enum DocAt
{
- TOP
- ,LEFT
+ TOP,
+ LEFT,
+ BOTTOM
};
public:
diff --git a/indra/llui/lldraghandle.cpp b/indra/llui/lldraghandle.cpp
index a93c666648..9f83fcca35 100644
--- a/indra/llui/lldraghandle.cpp
+++ b/indra/llui/lldraghandle.cpp
@@ -113,6 +113,7 @@ void LLDragHandleTop::setTitle(const std::string& title)
params.follows.flags(FOLLOWS_TOP | FOLLOWS_LEFT | FOLLOWS_RIGHT);
params.font_shadow(LLFontGL::DROP_SHADOW_SOFT);
params.use_ellipses = true;
+ params.allow_html = false; //cancel URL replacement in floater title
mTitleBox = LLUICtrlFactory::create<LLTextBox> (params);
addChild( mTitleBox );
}
@@ -247,15 +248,14 @@ void LLDragHandleTop::reshapeTitleBox()
return;
}
const LLFontGL* font = LLFontGL::getFontSansSerif();
- S32 title_width = font->getWidth( mTitleBox->getText() ) + TITLE_HPAD;
- if (getMaxTitleWidth() > 0)
- title_width = llmin(title_width, getMaxTitleWidth());
+ S32 title_width = getRect().getWidth();
+ title_width -= LEFT_PAD + 2 * BORDER_PAD + getButtonsRect().getWidth();
S32 title_height = llround(font->getLineHeight());
LLRect title_rect;
title_rect.setLeftTopAndSize(
LEFT_PAD,
getRect().getHeight() - title_vpad,
- getRect().getWidth() - LEFT_PAD - RIGHT_PAD,
+ title_width,
title_height);
// calls reshape on mTitleBox
diff --git a/indra/llui/lldraghandle.h b/indra/llui/lldraghandle.h
index dc5410787b..825bc9303e 100644
--- a/indra/llui/lldraghandle.h
+++ b/indra/llui/lldraghandle.h
@@ -71,6 +71,8 @@ public:
BOOL getForeground() const { return mForeground; }
void setMaxTitleWidth(S32 max_width) {mMaxTitleWidth = llmin(max_width, mMaxTitleWidth); }
S32 getMaxTitleWidth() const { return mMaxTitleWidth; }
+ void setButtonsRect(const LLRect& rect){ mButtonsRect = rect; }
+ LLRect getButtonsRect() { return mButtonsRect; }
void setTitleVisible(BOOL visible);
virtual void setTitle( const std::string& title ) = 0;
@@ -88,6 +90,7 @@ protected:
LLTextBox* mTitleBox;
private:
+ LLRect mButtonsRect;
S32 mDragLastScreenX;
S32 mDragLastScreenY;
S32 mLastMouseScreenX;
diff --git a/indra/llui/lleditmenuhandler.cpp b/indra/llui/lleditmenuhandler.cpp
index 821afae8fd..245bce76f5 100644
--- a/indra/llui/lleditmenuhandler.cpp
+++ b/indra/llui/lleditmenuhandler.cpp
@@ -37,3 +37,10 @@
/* static */
LLEditMenuHandler* LLEditMenuHandler::gEditMenuHandler = NULL;
+LLEditMenuHandler::~LLEditMenuHandler()
+{
+ if (gEditMenuHandler == this)
+ {
+ gEditMenuHandler = NULL;
+ }
+}
diff --git a/indra/llui/lleditmenuhandler.h b/indra/llui/lleditmenuhandler.h
index 1de9c56afb..d72283cd99 100644
--- a/indra/llui/lleditmenuhandler.h
+++ b/indra/llui/lleditmenuhandler.h
@@ -38,7 +38,7 @@ class LLEditMenuHandler
{
public:
// this is needed even though this is just an interface class.
- virtual ~LLEditMenuHandler() {};
+ virtual ~LLEditMenuHandler();
virtual void undo() {};
virtual BOOL canUndo() const { return FALSE; }
diff --git a/indra/llui/llflatlistview.cpp b/indra/llui/llflatlistview.cpp
index 3694ecd4f4..ec247b25c3 100644
--- a/indra/llui/llflatlistview.cpp
+++ b/indra/llui/llflatlistview.cpp
@@ -1,10 +1,10 @@
/**
* @file llflatlistview.cpp
- * @brief LLFlatListView base class
+ * @brief LLFlatListView base class and extension to support messages for several cases of an empty list.
*
* $LicenseInfo:firstyear=2009&license=viewergpl$
*
- * Copyright (c) 2009, Linden Research, Inc.
+ * Copyright (c) 2009-2010, Linden Research, Inc.
*
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
@@ -42,8 +42,6 @@ static const LLDefaultChildRegistry::Register<LLFlatListView> flat_list_view("fl
const LLSD SELECTED_EVENT = LLSD().with("selected", true);
const LLSD UNSELECTED_EVENT = LLSD().with("selected", false);
-static const std::string COMMENT_TEXTBOX = "comment_text";
-
//forward declaration
bool llsds_are_equal(const LLSD& llsd_1, const LLSD& llsd_2);
@@ -51,7 +49,8 @@ LLFlatListView::Params::Params()
: item_pad("item_pad"),
allow_select("allow_select"),
multi_select("multi_select"),
- keep_one_selected("keep_one_selected")
+ keep_one_selected("keep_one_selected"),
+ no_items_text("no_items_text")
{};
void LLFlatListView::reshape(S32 width, S32 height, BOOL called_from_parent /* = TRUE */)
@@ -244,7 +243,7 @@ LLUUID LLFlatListView::getSelectedUUID() const
}
}
-void LLFlatListView::getSelectedUUIDs(std::vector<LLUUID>& selected_uuids) const
+void LLFlatListView::getSelectedUUIDs(uuid_vec_t& selected_uuids) const
{
if (mSelectedItemPairs.empty()) return;
@@ -289,25 +288,12 @@ void LLFlatListView::resetSelection(bool no_commit_on_deselection /*= false*/)
onCommit();
}
- // Stretch selected items rect to ensure it won't be clipped
- mSelectedItemsBorder->setRect(getSelectedItemsRect().stretch(-1));
+ // Stretch selected item rect to ensure it won't be clipped
+ mSelectedItemsBorder->setRect(getLastSelectedItemRect().stretch(-1));
}
void LLFlatListView::setNoItemsCommentText(const std::string& comment_text)
{
- if (NULL == mNoItemsCommentTextbox)
- {
- LLRect comment_rect = getRect();
- comment_rect.setOriginAndSize(0, 0, comment_rect.getWidth(), comment_rect.getHeight());
- comment_rect.stretch(-getBorderWidth());
- LLTextBox::Params text_p;
- text_p.name(COMMENT_TEXTBOX);
- text_p.border_visible(false);
- text_p.rect(comment_rect);
- text_p.follows.flags(FOLLOWS_ALL);
- mNoItemsCommentTextbox = LLUICtrlFactory::create<LLTextBox>(text_p, this);
- }
-
mNoItemsCommentTextbox->setValue(comment_text);
}
@@ -361,7 +347,6 @@ bool LLFlatListView::updateValue(const LLSD& old_value, const LLSD& new_value)
// PROTECTED STUFF
//////////////////////////////////////////////////////////////////////////
-
LLFlatListView::LLFlatListView(const LLFlatListView::Params& p)
: LLScrollContainer(p)
, mItemComparator(NULL)
@@ -393,11 +378,30 @@ LLFlatListView::LLFlatListView(const LLFlatListView::Params& p)
LLViewBorder::Params params;
params.name("scroll border");
- params.rect(getSelectedItemsRect());
+ params.rect(getLastSelectedItemRect());
params.visible(false);
params.bevel_style(LLViewBorder::BEVEL_IN);
mSelectedItemsBorder = LLUICtrlFactory::create<LLViewBorder> (params);
mItemsPanel->addChild( mSelectedItemsBorder );
+
+ {
+ // create textbox for "No Items" comment text
+ LLTextBox::Params text_p = p.no_items_text;
+ if (!text_p.rect.isProvided())
+ {
+ LLRect comment_rect = getRect();
+ comment_rect.setOriginAndSize(0, 0, comment_rect.getWidth(), comment_rect.getHeight());
+ comment_rect.stretch(-getBorderWidth());
+ text_p.rect(comment_rect);
+ }
+ text_p.border_visible(false);
+
+ if (!text_p.follows.isProvided())
+ {
+ text_p.follows.flags(FOLLOWS_ALL);
+ }
+ mNoItemsCommentTextbox = LLUICtrlFactory::create<LLTextBox>(text_p, this);
+ }
};
// virtual
@@ -480,21 +484,88 @@ void LLFlatListView::rearrangeItems()
item_new_top -= (rc.getHeight() + mItemPad);
}
- // Stretch selected items rect to ensure it won't be clipped
- mSelectedItemsBorder->setRect(getSelectedItemsRect().stretch(-1));
+ // Stretch selected item rect to ensure it won't be clipped
+ mSelectedItemsBorder->setRect(getLastSelectedItemRect().stretch(-1));
}
void LLFlatListView::onItemMouseClick(item_pair_t* item_pair, MASK mask)
{
if (!item_pair) return;
+ if (!item_pair->first)
+ {
+ llwarning("Attempt to selet an item pair containing null panel item", 0);
+ return;
+ }
+
setFocus(TRUE);
bool select_item = !isSelected(item_pair);
//*TODO find a better place for that enforcing stuff
if (mKeepOneItemSelected && numSelected() == 1 && !select_item) return;
-
+
+ if ( (mask & MASK_SHIFT) && !(mask & MASK_CONTROL)
+ && mMultipleSelection && !mSelectedItemPairs.empty() )
+ {
+ item_pair_t* last_selected_pair = mSelectedItemPairs.back();
+
+ // If item_pair is already selected - do nothing
+ if (last_selected_pair == item_pair)
+ return;
+
+ bool grab_items = false;
+ pairs_list_t pairs_to_select;
+
+ // Pick out items from list between last selected and current clicked item_pair.
+ for (pairs_iterator_t
+ iter = mItemPairs.begin(),
+ iter_end = mItemPairs.end();
+ iter != iter_end; ++iter)
+ {
+ item_pair_t* cur = *iter;
+ if (cur == last_selected_pair || cur == item_pair)
+ {
+ grab_items = !grab_items;
+ // Skip last selected and current clicked item pairs.
+ continue;
+ }
+ if (!cur->first->getVisible())
+ {
+ // Skip invisible item pairs.
+ continue;
+ }
+ if (grab_items)
+ {
+ pairs_to_select.push_back(cur);
+ }
+ }
+
+ if (select_item)
+ {
+ pairs_to_select.push_back(item_pair);
+ }
+
+ for (pairs_iterator_t
+ iter = pairs_to_select.begin(),
+ iter_end = pairs_to_select.end();
+ iter != iter_end; ++iter)
+ {
+ item_pair_t* pair_to_select = *iter;
+ selectItemPair(pair_to_select, true);
+ }
+
+ if (!select_item)
+ {
+ // Item was already selected but there is a need to update last selected item and its border.
+ // Do it here to prevent extra mCommitOnSelectionChange in selectItemPair().
+ mSelectedItemPairs.remove(item_pair);
+ mSelectedItemPairs.push_back(item_pair);
+ mSelectedItemsBorder->setRect(getLastSelectedItemRect().stretch(-1));
+ }
+ return;
+ }
+
if (!(mask & MASK_CONTROL) || !mMultipleSelection) resetSelection();
selectItemPair(item_pair, select_item);
}
@@ -548,15 +619,6 @@ BOOL LLFlatListView::handleKeyHere(KEY key, MASK mask)
}
break;
}
- case 'A':
- {
- if(MASK_CONTROL & mask)
- {
- selectAll();
- handled = TRUE;
- }
- break;
- }
default:
break;
}
@@ -664,8 +726,8 @@ bool LLFlatListView::selectItemPair(item_pair_t* item_pair, bool select)
onCommit();
}
- // Stretch selected items rect to ensure it won't be clipped
- mSelectedItemsBorder->setRect(getSelectedItemsRect().stretch(-1));
+ // Stretch selected item rect to ensure it won't be clipped
+ mSelectedItemsBorder->setRect(getLastSelectedItemRect().stretch(-1));
return true;
}
@@ -680,31 +742,20 @@ LLRect LLFlatListView::getLastSelectedItemRect()
return mSelectedItemPairs.back()->first->getRect();
}
-LLRect LLFlatListView::getSelectedItemsRect()
-{
- if (!mSelectedItemPairs.size())
- {
- return LLRect::null;
- }
- LLRect rc = getLastSelectedItemRect();
- for ( pairs_const_iterator_t
- it = mSelectedItemPairs.begin(),
- it_end = mSelectedItemPairs.end();
- it != it_end; ++it )
- {
- rc.unionWith((*it)->first->getRect());
- }
- return rc;
-}
-
void LLFlatListView::selectFirstItem ()
{
+ // No items - no actions!
+ if (mItemPairs.empty()) return;
+
selectItemPair(mItemPairs.front(), true);
ensureSelectedVisible();
}
void LLFlatListView::selectLastItem ()
{
+ // No items - no actions!
+ if (mItemPairs.empty()) return;
+
selectItemPair(mItemPairs.back(), true);
ensureSelectedVisible();
}
@@ -798,10 +849,15 @@ bool LLFlatListView::selectNextItemPair(bool is_up_direction, bool reset_selecti
return false;
}
-bool LLFlatListView::selectAll()
+BOOL LLFlatListView::canSelectAll() const
{
- if (!mAllowSelection)
- return false;
+ return !mItemPairs.empty() && mAllowSelection && mMultipleSelection;
+}
+
+void LLFlatListView::selectAll()
+{
+ if (!mAllowSelection || !mMultipleSelection)
+ return;
mSelectedItemPairs.clear();
@@ -819,10 +875,8 @@ bool LLFlatListView::selectAll()
onCommit();
}
- // Stretch selected items rect to ensure it won't be clipped
- mSelectedItemsBorder->setRect(getSelectedItemsRect().stretch(-1));
-
- return true;
+ // Stretch selected item rect to ensure it won't be clipped
+ mSelectedItemsBorder->setRect(getLastSelectedItemRect().stretch(-1));
}
bool LLFlatListView::isSelected(item_pair_t* item_pair) const
@@ -878,7 +932,11 @@ void LLFlatListView::notifyParentItemsRectChanged()
// take into account comment text height if exists
if (mNoItemsCommentTextbox && mNoItemsCommentTextbox->getVisible())
{
+ // top text padding inside the textbox is included into the height
comment_height = mNoItemsCommentTextbox->getTextPixelHeight();
+
+ // take into account a distance from parent's top border to textbox's top
+ comment_height += getRect().getHeight() - mNoItemsCommentTextbox->getRect().mTop;
}
LLRect req_rect = getItemsRect();
@@ -909,6 +967,10 @@ void LLFlatListView::setNoItemsCommentVisible(bool visible) const
{
if (visible)
{
+/*
+// *NOTE: MA 2010-02-04
+// Deprecated after params of the comment text box were moved into widget (flat_list_view.xml)
+// can be removed later if nothing happened.
// We have to update child rect here because of issues with rect after reshaping while creating LLTextbox
// It is possible to have invalid LLRect if Flat List is in LLAccordionTab
LLRect comment_rect = getLocalRect();
@@ -920,6 +982,7 @@ void LLFlatListView::setNoItemsCommentVisible(bool visible) const
LLViewBorder* scroll_border = getChild<LLViewBorder>("scroll border");
comment_rect.stretch(-scroll_border->getBorderWidth());
mNoItemsCommentTextbox->setRect(comment_rect);
+*/
}
mNoItemsCommentTextbox->setVisible(visible);
}
@@ -951,11 +1014,17 @@ void LLFlatListView::getValues(std::vector<LLSD>& values) const
void LLFlatListView::onFocusReceived()
{
mSelectedItemsBorder->setVisible(TRUE);
+ gEditMenuHandler = this;
}
// virtual
void LLFlatListView::onFocusLost()
{
mSelectedItemsBorder->setVisible(FALSE);
+ // Route menu back to the default
+ if( gEditMenuHandler == this )
+ {
+ gEditMenuHandler = NULL;
+ }
}
//virtual
@@ -1059,4 +1128,43 @@ void LLFlatListView::detachItems(std::vector<LLPanel*>& detached_items)
}
}
+
+/************************************************************************/
+/* LLFlatListViewEx implementation */
+/************************************************************************/
+LLFlatListViewEx::Params::Params()
+: no_items_msg("no_items_msg")
+, no_filtered_items_msg("no_filtered_items_msg")
+{
+
+}
+
+LLFlatListViewEx::LLFlatListViewEx(const Params& p)
+: LLFlatListView(p)
+, mNoFilteredItemsMsg(p.no_filtered_items_msg)
+, mNoItemsMsg(p.no_items_msg)
+{
+
+}
+
+void LLFlatListViewEx::updateNoItemsMessage(const std::string& filter_string)
+{
+ bool items_filtered = !filter_string.empty();
+ if (items_filtered)
+ {
+ // items were filtered
+ LLStringUtil::format_map_t args;
+ args["[SEARCH_TERM]"] = LLURI::escape(filter_string);
+ std::string text = mNoFilteredItemsMsg;
+ LLStringUtil::format(text, args);
+ setNoItemsCommentText(text);
+ }
+ else
+ {
+ // list does not contain any items at all
+ setNoItemsCommentText(mNoItemsMsg);
+ }
+
+}
+
//EOF
diff --git a/indra/llui/llflatlistview.h b/indra/llui/llflatlistview.h
index 5999e79f61..4f718ab0dc 100644
--- a/indra/llui/llflatlistview.h
+++ b/indra/llui/llflatlistview.h
@@ -1,10 +1,10 @@
/**
* @file llflatlistview.h
- * @brief LLFlatListView base class
+ * @brief LLFlatListView base class and extension to support messages for several cases of an empty list.
*
* $LicenseInfo:firstyear=2009&license=viewergpl$
*
- * Copyright (c) 2009, Linden Research, Inc.
+ * Copyright (c) 2009-2010, Linden Research, Inc.
*
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
@@ -35,8 +35,8 @@
#include "llpanel.h"
#include "llscrollcontainer.h"
+#include "lltextbox.h"
-class LLTextBox;
/**
* LLFlatListView represents a flat list ui control that operates on items in a form of LLPanel's.
@@ -58,7 +58,7 @@ class LLTextBox;
* - Order of returned selected items are not guaranteed
* - The control assumes that all items being added are unique.
*/
-class LLFlatListView : public LLScrollContainer
+class LLFlatListView : public LLScrollContainer, public LLEditMenuHandler
{
public:
@@ -108,11 +108,12 @@ public:
/** padding between items */
Optional<U32> item_pad;
+ /** textbox with info message when list is empty*/
+ Optional<LLTextBox::Params> no_items_text;
+
Params();
};
- virtual ~LLFlatListView() { clear(); };
-
/**
* Connects callback to signal called when Return key is pressed.
*/
@@ -221,7 +222,7 @@ public:
* 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;
+ virtual void getSelectedUUIDs(uuid_vec_t& selected_uuids) const;
/** Get the top selected item */
virtual LLPanel* getSelectedItem() const;
@@ -341,7 +342,8 @@ protected:
virtual bool selectNextItemPair(bool is_up_direction, bool reset_selection);
- virtual bool selectAll();
+ virtual BOOL canSelectAll() const;
+ virtual void selectAll();
virtual bool isSelected(item_pair_t* item_pair) const;
@@ -368,8 +370,6 @@ protected:
LLRect getLastSelectedItemRect();
- LLRect getSelectedItemsRect();
-
void ensureSelectedVisible();
private:
@@ -378,11 +378,14 @@ private:
void setNoItemsCommentVisible(bool visible) const;
-private:
+protected:
/** Comparator to use when sorting the list. */
const ItemComparator* mItemComparator;
+
+private:
+
LLPanel* mItemsPanel;
S32 mItemsNoScrollWidth;
@@ -427,4 +430,54 @@ private:
commit_signal_t mOnReturnSignal;
};
+/**
+ * Extends LLFlatListView functionality to show different messages when there are no items in the
+ * list depend on whether they are filtered or not.
+ *
+ * Class provides one message per case of empty list.
+ * It also provides protected updateNoItemsMessage() method to be called each time when derived list
+ * is changed to update base mNoItemsCommentTextbox value.
+ *
+ * It is implemented to avoid duplication of this functionality in concrete implementations of the
+ * lists. It is intended to be used as a base class for lists which should support two different
+ * messages for empty state. Can be improved to support more than two messages via state-to-message map.
+ */
+class LLFlatListViewEx : public LLFlatListView
+{
+public:
+ struct Params : public LLInitParam::Block<Params, LLFlatListView::Params>
+ {
+ /**
+ * Contains a message for empty list when it does not contain any items at all.
+ */
+ Optional<std::string> no_items_msg;
+
+ /**
+ * Contains a message for empty list when its items are removed by filtering.
+ */
+ Optional<std::string> no_filtered_items_msg;
+ Params();
+ };
+
+ // *WORKAROUND: two methods to overload appropriate Params due to localization issue:
+ // no_items_msg & no_filtered_items_msg attributes are not defined as translatable in VLT. See EXT-5931
+ void setNoItemsMsg(const std::string& msg) { mNoItemsMsg = msg; }
+ void setNoFilteredItemsMsg(const std::string& msg) { mNoFilteredItemsMsg = msg; }
+
+protected:
+ LLFlatListViewEx(const Params& p);
+
+ /**
+ * Applies a message for empty list depend on passed argument.
+ *
+ * @param filter_string - if is not empty, message for filtered items will be set, otherwise for
+ * completely empty list. Value of filter string will be passed as search_term in SLURL.
+ */
+ void updateNoItemsMessage(const std::string& filter_string);
+
+private:
+ std::string mNoFilteredItemsMsg;
+ std::string mNoItemsMsg;
+};
+
#endif
diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp
index 79d8f90fec..79c47a1136 100644
--- a/indra/llui/llfloater.cpp
+++ b/indra/llui/llfloater.cpp
@@ -346,7 +346,7 @@ void LLFloater::layoutDragHandle()
rect = getLocalRect();
}
mDragHandle->setRect(rect);
- updateButtons();
+ updateTitleButtons();
applyTitle();
}
@@ -527,10 +527,7 @@ void LLFloater::setVisible( BOOL visible )
if( !visible )
{
- if( gFocusMgr.childIsTopCtrl( this ) )
- {
- gFocusMgr.setTopCtrl(NULL);
- }
+ LLUI::removePopup(this);
if( gFocusMgr.childHasMouseCapture( this ) )
{
@@ -566,6 +563,7 @@ void LLFloater::handleVisibilityChange ( BOOL new_visibility )
void LLFloater::openFloater(const LLSD& key)
{
+ llinfos << "Opening floater " << getName() << llendl;
mKey = key; // in case we need to open ourselves again
if (getSoundFlags() != SILENT
@@ -606,6 +604,7 @@ void LLFloater::openFloater(const LLSD& key)
void LLFloater::closeFloater(bool app_quitting)
{
+ llinfos << "Closing floater " << getName() << llendl;
if (app_quitting)
{
LLFloater::sQuitting = true;
@@ -704,10 +703,7 @@ void LLFloater::reshape(S32 width, S32 height, BOOL called_from_parent)
void LLFloater::releaseFocus()
{
- if( gFocusMgr.childIsTopCtrl( this ) )
- {
- gFocusMgr.setTopCtrl(NULL);
- }
+ LLUI::removePopup(this);
setFocus(FALSE);
@@ -1067,11 +1063,10 @@ void LLFloater::setMinimized(BOOL minimize)
// Reshape *after* setting mMinimized
reshape( mExpandedRect.getWidth(), mExpandedRect.getHeight(), TRUE );
}
-
- applyTitle ();
make_ui_sound("UISndWindowClose");
- updateButtons();
+ updateTitleButtons();
+ applyTitle ();
}
void LLFloater::setFocus( BOOL b )
@@ -1127,6 +1122,7 @@ void LLFloater::setIsChrome(BOOL is_chrome)
setFocus(FALSE);
// can't Ctrl-Tab to "chrome" floaters
setFocusRoot(FALSE);
+ mButtons[BUTTON_CLOSE]->setToolTip(LLStringExplicit(getButtonTooltip(Params(), BUTTON_CLOSE, is_chrome)));
}
// no titles displayed on "chrome" floaters
@@ -1196,7 +1192,7 @@ void LLFloater::setHost(LLMultiFloater* host)
mButtonScale = 1.f;
//mButtonsEnabled[BUTTON_TEAR_OFF] = FALSE;
}
- updateButtons();
+ updateTitleButtons();
if (host)
{
mHostHandle = host->getHandle();
@@ -1360,7 +1356,6 @@ void LLFloater::bringToFront( S32 x, S32 y )
// virtual
void LLFloater::setVisibleAndFrontmost(BOOL take_focus)
{
- gFocusMgr.setTopCtrl(NULL);
setVisible(TRUE);
setFrontmost(take_focus);
}
@@ -1396,7 +1391,7 @@ void LLFloater::setCanDock(bool b)
mButtonsEnabled[BUTTON_DOCK] = FALSE;
}
}
- updateButtons();
+ updateTitleButtons();
}
void LLFloater::setDocked(bool docked, bool pop_on_undock)
@@ -1405,7 +1400,7 @@ void LLFloater::setDocked(bool docked, bool pop_on_undock)
{
mDocked = docked;
mButtonsEnabled[BUTTON_DOCK] = !mDocked;
- updateButtons();
+ updateTitleButtons();
storeDockStateControl();
}
@@ -1458,7 +1453,7 @@ void LLFloater::onClickTearOff(LLFloater* self)
}
self->setTornOff(false);
}
- self->updateButtons();
+ self->updateTitleButtons();
}
// static
@@ -1554,7 +1549,12 @@ void LLFloater::onClickClose( LLFloater* self )
{
if (!self)
return;
- self->closeFloater(false);
+ self->onClickCloseBtn();
+}
+
+void LLFloater::onClickCloseBtn()
+{
+ closeFloater(false);
}
@@ -1565,43 +1565,34 @@ void LLFloater::draw()
// draw background
if( isBackgroundVisible() )
{
+ drawShadow(this);
+
S32 left = LLPANEL_BORDER_WIDTH;
S32 top = getRect().getHeight() - LLPANEL_BORDER_WIDTH;
S32 right = getRect().getWidth() - LLPANEL_BORDER_WIDTH;
S32 bottom = LLPANEL_BORDER_WIDTH;
- static LLUICachedControl<S32> shadow_offset_S32 ("DropShadowFloater", 0);
- static LLUIColor shadow_color_cached = LLUIColorTable::instance().getColor("ColorDropShadow");
- LLColor4 shadow_color = shadow_color_cached;
- F32 shadow_offset = (F32)shadow_offset_S32;
-
- if (!isBackgroundOpaque())
- {
- shadow_offset *= 0.2f;
- shadow_color.mV[VALPHA] *= 0.5f;
- }
- gl_drop_shadow(left, top, right, bottom,
- shadow_color % alpha,
- llround(shadow_offset));
-
LLUIImage* image = NULL;
LLColor4 color;
+ LLColor4 overlay_color;
if (isBackgroundOpaque())
{
// NOTE: image may not be set
image = getBackgroundImage();
color = getBackgroundColor();
+ overlay_color = getBackgroundImageOverlay();
}
else
{
image = getTransparentImage();
color = getTransparentColor();
+ overlay_color = getTransparentImageOverlay();
}
if (image)
{
// We're using images for this floater's backgrounds
- image->draw(getLocalRect(), UI_VERTEX_COLOR % alpha);
+ image->draw(getLocalRect(), overlay_color % alpha);
}
else
{
@@ -1650,24 +1641,8 @@ void LLFloater::draw()
}
else
{
- //FIXME: get rid of this hack
- // draw children
- LLView* focused_child = dynamic_cast<LLView*>(gFocusMgr.getKeyboardFocus());
- BOOL focused_child_visible = FALSE;
- if (focused_child && focused_child->getParent() == this)
- {
- focused_child_visible = focused_child->getVisible();
- focused_child->setVisible(FALSE);
- }
-
// don't call LLPanel::draw() since we've implemented custom background rendering
LLView::draw();
-
- if (focused_child_visible)
- {
- focused_child->setVisible(TRUE);
- }
- drawChild(focused_child);
}
// update tearoff button for torn off floaters
@@ -1682,6 +1657,29 @@ void LLFloater::draw()
}
}
+void LLFloater::drawShadow(LLPanel* panel)
+{
+ F32 alpha = panel->getDrawContext().mAlpha;
+ S32 left = LLPANEL_BORDER_WIDTH;
+ S32 top = panel->getRect().getHeight() - LLPANEL_BORDER_WIDTH;
+ S32 right = panel->getRect().getWidth() - LLPANEL_BORDER_WIDTH;
+ S32 bottom = LLPANEL_BORDER_WIDTH;
+
+ static LLUICachedControl<S32> shadow_offset_S32 ("DropShadowFloater", 0);
+ static LLUIColor shadow_color_cached = LLUIColorTable::instance().getColor("ColorDropShadow");
+ LLColor4 shadow_color = shadow_color_cached;
+ F32 shadow_offset = (F32)shadow_offset_S32;
+
+ if (!panel->isBackgroundOpaque())
+ {
+ shadow_offset *= 0.2f;
+ shadow_color.mV[VALPHA] *= 0.5f;
+ }
+ gl_drop_shadow(left, top, right, bottom,
+ shadow_color % alpha,
+ llround(shadow_offset));
+}
+
void LLFloater::setCanMinimize(BOOL can_minimize)
{
// if removing minimize/restore button programmatically,
@@ -1695,7 +1693,7 @@ void LLFloater::setCanMinimize(BOOL can_minimize)
mButtonsEnabled[BUTTON_MINIMIZE] = can_minimize && !isMinimized();
mButtonsEnabled[BUTTON_RESTORE] = can_minimize && isMinimized();
- updateButtons();
+ updateTitleButtons();
}
void LLFloater::setCanClose(BOOL can_close)
@@ -1703,7 +1701,7 @@ void LLFloater::setCanClose(BOOL can_close)
mCanClose = can_close;
mButtonsEnabled[BUTTON_CLOSE] = can_close;
- updateButtons();
+ updateTitleButtons();
}
void LLFloater::setCanTearOff(BOOL can_tear_off)
@@ -1711,7 +1709,7 @@ void LLFloater::setCanTearOff(BOOL can_tear_off)
mCanTearOff = can_tear_off;
mButtonsEnabled[BUTTON_TEAR_OFF] = mCanTearOff && !mHostHandle.isDead();
- updateButtons();
+ updateTitleButtons();
}
@@ -1735,10 +1733,11 @@ void LLFloater::setCanDrag(BOOL can_drag)
}
}
-void LLFloater::updateButtons()
+void LLFloater::updateTitleButtons()
{
static LLUICachedControl<S32> floater_close_box_size ("UIFloaterCloseBoxSize", 0);
static LLUICachedControl<S32> close_box_from_top ("UICloseBoxFromTop", 0);
+ LLRect buttons_rect;
S32 button_count = 0;
for (S32 i = 0; i < BUTTON_COUNT; i++)
{
@@ -1789,6 +1788,18 @@ void LLFloater::updateButtons()
llround((F32)floater_close_box_size * mButtonScale));
}
+ // first time here, init 'buttons_rect'
+ if(1 == button_count)
+ {
+ buttons_rect = btn_rect;
+ }
+ else
+ {
+ // if mDragOnLeft=true then buttons are on top-left side vertically aligned
+ // title is not displayed in this case, calculating 'buttons_rect' for future use
+ mDragOnLeft ? buttons_rect.mBottom -= btn_rect.mBottom :
+ buttons_rect.mLeft = btn_rect.mLeft;
+ }
mButtons[i]->setRect(btn_rect);
mButtons[i]->setVisible(TRUE);
// the restore button should have a tab stop so that it takes action when you Ctrl-Tab to a minimized floater
@@ -1800,7 +1811,10 @@ void LLFloater::updateButtons()
}
}
if (mDragHandle)
- mDragHandle->setMaxTitleWidth(getRect().getWidth() - (button_count * (floater_close_box_size + 1)));
+ {
+ localRectToOtherView(buttons_rect, &buttons_rect, mDragHandle);
+ mDragHandle->setButtonsRect(buttons_rect);
+ }
}
void LLFloater::buildButtons(const Params& floater_params)
@@ -1848,7 +1862,7 @@ void LLFloater::buildButtons(const Params& floater_params)
p.click_callback.function(boost::bind(sButtonCallbacks[i], this));
p.tab_stop(false);
p.follows.flags(FOLLOWS_TOP|FOLLOWS_RIGHT);
- p.tool_tip = getButtonTooltip(floater_params, (EFloaterButton)i);
+ p.tool_tip = getButtonTooltip(floater_params, (EFloaterButton)i, getIsChrome());
p.scale_image(true);
p.chrome(true);
@@ -1857,7 +1871,7 @@ void LLFloater::buildButtons(const Params& floater_params)
mButtons[i] = buttonp;
}
- updateButtons();
+ updateTitleButtons();
}
// static
@@ -1903,8 +1917,15 @@ LLUIImage* LLFloater::getButtonPressedImage(const Params& p, EFloaterButton e)
}
// static
-std::string LLFloater::getButtonTooltip(const Params& p, EFloaterButton e)
+std::string LLFloater::getButtonTooltip(const Params& p, EFloaterButton e, bool is_chrome)
{
+ // EXT-4081 (Lag Meter: Ctrl+W does not close floater)
+ // If floater is chrome set 'Close' text for close button's tooltip
+ if(is_chrome && BUTTON_CLOSE == e)
+ {
+ static std::string close_tooltip_chrome = LLTrans::getString("BUTTON_CLOSE_CHROME");
+ return close_tooltip_chrome;
+ }
// TODO: per-floater localizable tooltips set in XML
return sButtonToolTips[e];
}
@@ -1916,9 +1937,10 @@ static LLDefaultChildRegistry::Register<LLFloaterView> r("floater_view");
LLFloaterView::LLFloaterView (const Params& p)
: LLUICtrl (p),
+
mFocusCycleMode(FALSE),
- mSnapOffsetBottom(0)
- ,mSnapOffsetRight(0)
+ mSnapOffsetBottom(0),
+ mSnapOffsetRight(0)
{
}
@@ -2391,10 +2413,17 @@ void LLFloaterView::adjustToFitScreen(LLFloater* floater, BOOL allow_partial_out
LLRect new_rect;
new_rect.setLeftTopAndSize(view_rect.mLeft,view_rect.mTop,new_width, new_height);
- floater->reshape( new_width, new_height, TRUE );
- floater->setRect(new_rect);
+ floater->setShape(new_rect);
- floater->translateIntoRect( getLocalRect(), false );
+ if (floater->followsRight())
+ {
+ floater->translate(old_width - new_width, 0);
+ }
+
+ if (floater->followsTop())
+ {
+ floater->translate(0, old_height - new_height);
+ }
}
}
@@ -2497,10 +2526,7 @@ void LLFloaterView::syncFloaterTabOrder()
if (modal_dialog)
{
// If we have a visible modal dialog, make sure that it has focus
- if( gFocusMgr.getTopCtrl() != modal_dialog )
- {
- gFocusMgr.setTopCtrl( modal_dialog );
- }
+ LLUI::addPopup(modal_dialog);
if( !gFocusMgr.childHasKeyboardFocus( modal_dialog ) )
{
@@ -2578,6 +2604,8 @@ void LLFloaterView::pushVisibleAll(BOOL visible, const skip_list_t& skip_list)
view->pushVisible(visible);
}
}
+
+ LLFloaterReg::blockShowFloaters(true);
}
void LLFloaterView::popVisibleAll(const skip_list_t& skip_list)
@@ -2595,6 +2623,8 @@ void LLFloaterView::popVisibleAll(const skip_list_t& skip_list)
view->popVisible();
}
}
+
+ LLFloaterReg::blockShowFloaters(false);
}
void LLFloater::setInstanceName(const std::string& name)
@@ -2805,3 +2835,15 @@ bool LLFloater::isShown(const LLFloater* floater)
{
return floater && floater->isShown();
}
+
+/* static */
+bool LLFloater::isMinimized(const LLFloater* floater)
+{
+ return floater && floater->isMinimized();
+}
+
+/* static */
+bool LLFloater::isVisible(const LLFloater* floater)
+{
+ return floater && floater->getVisible();
+}
diff --git a/indra/llui/llfloater.h b/indra/llui/llfloater.h
index f70495c0f0..444711de81 100644
--- a/indra/llui/llfloater.h
+++ b/indra/llui/llfloater.h
@@ -194,6 +194,8 @@ public:
/// The static isShown() can accept a NULL pointer (which of course
/// returns false). When non-NULL, it calls the non-static isShown().
static bool isShown(const LLFloater* floater);
+ static bool isVisible(const LLFloater* floater);
+ static bool isMinimized(const LLFloater* floater);
BOOL isFirstLook() { return mFirstLook; } // EXT-2653: This function is necessary to prevent overlapping for secondary showed toasts
BOOL isFrontmost();
BOOL isDependent() { return !mDependeeHandle.isDead(); }
@@ -222,6 +224,7 @@ public:
virtual BOOL handleScrollWheel(S32 x, S32 y, S32 mask);
virtual void draw();
+ virtual void drawShadow(LLPanel* panel);
virtual void onOpen(const LLSD& key) {}
virtual void onClose(bool app_quitting) {}
@@ -301,23 +304,33 @@ protected:
const LLRect& getExpandedRect() const { return mExpandedRect; }
void setAutoFocus(BOOL focus) { mAutoFocus = focus; } // whether to automatically take focus when opened
+ BOOL getAutoFocus() const { return mAutoFocus; }
LLDragHandle* getDragHandle() const { return mDragHandle; }
void destroy() { die(); } // Don't call this directly. You probably want to call closeFloater()
+ virtual void onClickCloseBtn();
+
+ virtual void updateTitleButtons();
+
private:
void setForeground(BOOL b); // called only by floaterview
void cleanupHandles(); // remove handles to dead floaters
void createMinimizeButton();
- void updateButtons();
void buildButtons(const Params& p);
// Images and tooltips are named in the XML, but we want to look them
// up by index.
static LLUIImage* getButtonImage(const Params& p, EFloaterButton e);
static LLUIImage* getButtonPressedImage(const Params& p, EFloaterButton e);
- static std::string getButtonTooltip(const Params& p, EFloaterButton e);
+ /**
+ * @params is_chrome - if floater is Chrome it means that floater will never get focus.
+ * Therefore it can't be closed with 'Ctrl+W'. So the tooltip text of close button( X )
+ * should be 'Close' not 'Close(Ctrl+W)' as for usual floaters.
+ */
+ static std::string getButtonTooltip(const Params& p, EFloaterButton e, bool is_chrome);
+
BOOL offerClickToButton(S32 x, S32 y, MASK mask, EFloaterButton index);
void addResizeCtrls();
void layoutResizeCtrls();
@@ -344,6 +357,7 @@ protected:
LLResizeBar* mResizeBar[4];
LLResizeHandle* mResizeHandle[4];
+ LLButton* mButtons[BUTTON_COUNT];
private:
LLRect mExpandedRect;
@@ -377,7 +391,6 @@ private:
handle_set_t mDependents;
bool mButtonsEnabled[BUTTON_COUNT];
- LLButton* mButtons[BUTTON_COUNT];
F32 mButtonScale;
BOOL mAutoFocus;
LLHandle<LLFloater> mSnappedTo;
@@ -419,11 +432,15 @@ private:
class LLFloaterView : public LLUICtrl
{
+public:
+ struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>{};
+
protected:
LLFloaterView (const Params& p);
friend class LLUICtrlFactory;
public:
+
/*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
void reshapeFloater(S32 width, S32 height, BOOL called_from_parent, BOOL adjust_vertical);
@@ -468,9 +485,6 @@ public:
void setSnapOffsetRight(S32 offset) { mSnapOffsetRight = offset; }
private:
- S32 mColumn;
- S32 mNextLeft;
- S32 mNextTop;
BOOL mFocusCycleMode;
S32 mSnapOffsetBottom;
S32 mSnapOffsetRight;
diff --git a/indra/llui/llfloaterreg.cpp b/indra/llui/llfloaterreg.cpp
index eb67e3a561..7588d8ab7a 100644
--- a/indra/llui/llfloaterreg.cpp
+++ b/indra/llui/llfloaterreg.cpp
@@ -34,6 +34,7 @@
#include "llfloaterreg.h"
+//#include "llagent.h"
#include "llfloater.h"
#include "llmultifloater.h"
#include "llfloaterreglistener.h"
@@ -45,6 +46,7 @@ LLFloaterReg::instance_list_t LLFloaterReg::sNullInstanceList;
LLFloaterReg::instance_map_t LLFloaterReg::sInstanceMap;
LLFloaterReg::build_map_t LLFloaterReg::sBuildMap;
std::map<std::string,std::string> LLFloaterReg::sGroupMap;
+bool LLFloaterReg::sBlockShowFloaters = false;
static LLFloaterRegListener sFloaterRegListener;
@@ -217,6 +219,8 @@ LLFloaterReg::const_instance_list_t& LLFloaterReg::getFloaterList(const std::str
//static
LLFloater* LLFloaterReg::showInstance(const std::string& name, const LLSD& key, BOOL focus)
{
+ if( sBlockShowFloaters )
+ return 0;//
LLFloater* instance = getInstance(name, key);
if (instance)
{
@@ -268,11 +272,11 @@ bool LLFloaterReg::toggleInstance(const std::string& name, const LLSD& key)
}
//static
-// returns true if the instance exists and is visible
+// returns true if the instance exists and is visible (doesnt matter minimized or not)
bool LLFloaterReg::instanceVisible(const std::string& name, const LLSD& key)
{
LLFloater* instance = findInstance(name, key);
- return LLFloater::isShown(instance);
+ return LLFloater::isVisible(instance);
}
//static
@@ -459,3 +463,12 @@ bool LLFloaterReg::floaterInstanceVisible(const LLSD& sdname)
return instanceVisible(name, key);
}
+//static
+bool LLFloaterReg::floaterInstanceMinimized(const LLSD& sdname)
+{
+ LLSD key;
+ std::string name = sdname.asString();
+ parse_name_key(name, key);
+ LLFloater* instance = findInstance(name, key);
+ return LLFloater::isShown(instance);
+}
diff --git a/indra/llui/llfloaterreg.h b/indra/llui/llfloaterreg.h
index 634a235926..5cacf76771 100644
--- a/indra/llui/llfloaterreg.h
+++ b/indra/llui/llfloaterreg.h
@@ -75,6 +75,7 @@ private:
static instance_map_t sInstanceMap;
static build_map_t sBuildMap;
static std::map<std::string,std::string> sGroupMap;
+ static bool sBlockShowFloaters;
public:
// Registration
@@ -133,6 +134,7 @@ public:
static void hideFloaterInstance(const LLSD& sdname);
static void toggleFloaterInstance(const LLSD& sdname);
static bool floaterInstanceVisible(const LLSD& sdname);
+ static bool floaterInstanceMinimized(const LLSD& sdname);
// Typed find / get / show
template <class T>
@@ -152,6 +154,8 @@ public:
{
return dynamic_cast<T*>(showInstance(name, key, focus));
}
+
+ static void blockShowFloaters(bool value) { sBlockShowFloaters = value;}
};
diff --git a/indra/llui/llfocusmgr.cpp b/indra/llui/llfocusmgr.cpp
index 35fbc7b0a8..b3af258456 100644
--- a/indra/llui/llfocusmgr.cpp
+++ b/indra/llui/llfocusmgr.cpp
@@ -38,8 +38,6 @@
const F32 FOCUS_FADE_TIME = 0.3f;
-// NOTE: the LLFocusableElement implementation has been moved here from lluictrl.cpp.
-
LLFocusableElement::LLFocusableElement()
: mFocusLostCallback(NULL),
mFocusReceivedCallback(NULL),
@@ -124,8 +122,7 @@ boost::signals2::connection LLFocusableElement::setTopLostCallback(const focus_s
LLFocusMgr gFocusMgr;
LLFocusMgr::LLFocusMgr()
- :
- mLockedView( NULL ),
+: mLockedView( NULL ),
mMouseCaptor( NULL ),
mKeyboardFocus( NULL ),
mLastKeyboardFocus( NULL ),
@@ -133,16 +130,11 @@ LLFocusMgr::LLFocusMgr()
mKeystrokesOnly(FALSE),
mTopCtrl( NULL ),
mAppHasFocus(TRUE) // Macs don't seem to notify us that we've gotten focus, so default to true
- #ifdef _DEBUG
- , mMouseCaptorName("none")
- , mKeyboardFocusName("none")
- , mTopCtrlName("none")
- #endif
{
}
-void LLFocusMgr::releaseFocusIfNeeded( const LLView* view )
+void LLFocusMgr::releaseFocusIfNeeded( LLView* view )
{
if( childHasMouseCapture( view ) )
{
@@ -162,10 +154,7 @@ void LLFocusMgr::releaseFocusIfNeeded( const LLView* view )
}
}
- if( childIsTopCtrl( view ) )
- {
- setTopCtrl( NULL );
- }
+ LLUI::removePopup(view);
}
@@ -248,11 +237,6 @@ void LLFocusMgr::setKeyboardFocus(LLFocusableElement* new_focus, BOOL lock, BOOL
return;
}
- #ifdef _DEBUG
- LLUICtrl* focus_ctrl = dynamic_cast<LLUICtrl*>(new_focus);
- mKeyboardFocusName = focus_ctrl ? focus_ctrl->getName() : std::string("none");
- #endif
-
// If we've got a default keyboard focus, and the caller is
// releasing keyboard focus, move to the default.
if (mDefaultKeyboardFocus != NULL && mKeyboardFocus == NULL)
@@ -334,20 +318,12 @@ void LLFocusMgr::removeKeyboardFocusWithoutCallback( const LLFocusableElement* f
if( mKeyboardFocus == focus )
{
mKeyboardFocus = NULL;
- #ifdef _DEBUG
- mKeyboardFocusName = std::string("none");
- #endif
}
}
void LLFocusMgr::setMouseCapture( LLMouseHandler* new_captor )
{
- //if (mFocusLocked)
- //{
- // return;
- //}
-
if( new_captor != mMouseCaptor )
{
LLMouseHandler* old_captor = mMouseCaptor;
@@ -370,24 +346,14 @@ void LLFocusMgr::setMouseCapture( LLMouseHandler* new_captor )
old_captor->onMouseCaptureLost();
}
- #ifdef _DEBUG
- mMouseCaptorName = new_captor ? new_captor->getName() : std::string("none");
- #endif
}
}
void LLFocusMgr::removeMouseCaptureWithoutCallback( const LLMouseHandler* captor )
{
- //if (mFocusLocked)
- //{
- // return;
- //}
if( mMouseCaptor == captor )
{
mMouseCaptor = NULL;
- #ifdef _DEBUG
- mMouseCaptorName = std::string("none");
- #endif
}
}
@@ -416,10 +382,6 @@ void LLFocusMgr::setTopCtrl( LLUICtrl* new_top )
{
mTopCtrl = new_top;
- #ifdef _DEBUG
- mTopCtrlName = new_top ? new_top->getName() : std::string("none");
- #endif
-
if (old_top)
{
old_top->onTopLost();
@@ -432,9 +394,6 @@ void LLFocusMgr::removeTopCtrlWithoutCallback( const LLUICtrl* top_view )
if( mTopCtrl == top_view )
{
mTopCtrl = NULL;
- #ifdef _DEBUG
- mTopCtrlName = std::string("none");
- #endif
}
}
@@ -478,9 +437,9 @@ void LLFocusMgr::setAppHasFocus(BOOL focus)
}
// release focus from "top ctrl"s, which generally hides them
- if (!focus && mTopCtrl)
+ if (!focus)
{
- setTopCtrl(NULL);
+ LLUI::clearPopups();
}
mAppHasFocus = focus;
}
diff --git a/indra/llui/llfocusmgr.h b/indra/llui/llfocusmgr.h
index 83ecd1d301..86d3ccf111 100644
--- a/indra/llui/llfocusmgr.h
+++ b/indra/llui/llfocusmgr.h
@@ -65,10 +65,10 @@ public:
virtual BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent);
virtual BOOL handleUnicodeChar(llwchar uni_char, BOOL called_from_parent);
+ virtual void onTopLost(); // called when registered as top ctrl and user clicks elsewhere
protected:
virtual void onFocusReceived();
virtual void onFocusLost();
- virtual void onTopLost(); // called when registered as top ctrl and user clicks elsewhere
focus_signal_t* mFocusLostCallback;
focus_signal_t* mFocusReceivedCallback;
focus_signal_t* mFocusChangedCallback;
@@ -119,7 +119,7 @@ public:
BOOL childIsTopCtrl( const LLView* parent ) const;
// All Three
- void releaseFocusIfNeeded( const LLView* top_view );
+ void releaseFocusIfNeeded( LLView* top_view );
void lockFocus();
void unlockFocus();
BOOL focusLocked() const { return mLockedView != NULL; }
@@ -149,12 +149,6 @@ private:
typedef std::map<LLHandle<LLView>, LLHandle<LLView> > focus_history_map_t;
focus_history_map_t mFocusHistory;
-
- #ifdef _DEBUG
- std::string mMouseCaptorName;
- std::string mKeyboardFocusName;
- std::string mTopCtrlName;
- #endif
};
extern LLFocusMgr gFocusMgr;
diff --git a/indra/llui/llhandle.h b/indra/llui/llhandle.h
index 899f6b9326..8ade327044 100644
--- a/indra/llui/llhandle.h
+++ b/indra/llui/llhandle.h
@@ -67,6 +67,13 @@ public:
return *this;
}
+ template<typename Subclass>
+ LLHandle<T>& operator =(const LLHandle<Subclass>& other)
+ {
+ mTombStone = other.mTombStone;
+ return *this;
+ }
+
bool isDead() const
{
return mTombStone->getTarget() == NULL;
diff --git a/indra/llui/lliconctrl.h b/indra/llui/lliconctrl.h
index 66368f979b..7e37600409 100644
--- a/indra/llui/lliconctrl.h
+++ b/indra/llui/lliconctrl.h
@@ -73,6 +73,7 @@ public:
std::string getImageName() const;
void setColor(const LLColor4& color) { mColor = color; }
+ void setImage(LLPointer<LLUIImage> image) { mImagep = image; }
private:
void setIconImageDrawSize() ;
diff --git a/indra/llui/llkeywords.cpp b/indra/llui/llkeywords.cpp
index ede32084d0..75342afbe2 100644
--- a/indra/llui/llkeywords.cpp
+++ b/indra/llui/llkeywords.cpp
@@ -218,6 +218,86 @@ void LLKeywords::addToken(LLKeywordToken::TOKEN_TYPE type,
llassert(0);
}
}
+LLKeywords::WStringMapIndex::WStringMapIndex(const WStringMapIndex& other)
+{
+ if(other.mOwner)
+ {
+ copyData(other.mData, other.mLength);
+ }
+ else
+ {
+ mOwner = false;
+ mLength = other.mLength;
+ mData = other.mData;
+ }
+}
+
+LLKeywords::WStringMapIndex::WStringMapIndex(const LLWString& str)
+{
+ copyData(str.data(), str.size());
+}
+
+LLKeywords::WStringMapIndex::WStringMapIndex(const llwchar *start, size_t length):
+mData(start), mLength(length), mOwner(false)
+{
+}
+
+LLKeywords::WStringMapIndex::~WStringMapIndex()
+{
+ if(mOwner)
+ delete[] mData;
+}
+
+void LLKeywords::WStringMapIndex::copyData(const llwchar *start, size_t length)
+{
+ llwchar *data = new llwchar[length];
+ memcpy((void*)data, (const void*)start, length * sizeof(llwchar));
+
+ mOwner = true;
+ mLength = length;
+ mData = data;
+}
+
+bool LLKeywords::WStringMapIndex::operator<(const LLKeywords::WStringMapIndex &other) const
+{
+ // NOTE: Since this is only used to organize a std::map, it doesn't matter if it uses correct collate order or not.
+ // The comparison only needs to strictly order all possible strings, and be stable.
+
+ bool result = false;
+ const llwchar* self_iter = mData;
+ const llwchar* self_end = mData + mLength;
+ const llwchar* other_iter = other.mData;
+ const llwchar* other_end = other.mData + other.mLength;
+
+ while(true)
+ {
+ if(other_iter >= other_end)
+ {
+ // We've hit the end of other.
+ // This covers two cases: other being shorter than self, or the strings being equal.
+ // In either case, we want to return false.
+ result = false;
+ break;
+ }
+ else if(self_iter >= self_end)
+ {
+ // self is shorter than other.
+ result = true;
+ break;
+ }
+ else if(*self_iter != *other_iter)
+ {
+ // The current character differs. The strings are not equal.
+ result = *self_iter < *other_iter;
+ break;
+ }
+
+ self_iter++;
+ other_iter++;
+ }
+
+ return result;
+}
LLColor3 LLKeywords::readColor( const std::string& s )
{
@@ -429,7 +509,7 @@ void LLKeywords::findSegments(std::vector<LLTextSegmentPtr>* seg_list, const LLW
S32 seg_len = p - cur;
if( seg_len > 0 )
{
- LLWString word( cur, 0, seg_len );
+ WStringMapIndex word( cur, seg_len );
word_token_map_t::iterator map_iter = mWordTokenMap.find(word);
if( map_iter != mWordTokenMap.end() )
{
diff --git a/indra/llui/llkeywords.h b/indra/llui/llkeywords.h
index 53377869ca..e5b66dfa56 100644
--- a/indra/llui/llkeywords.h
+++ b/indra/llui/llkeywords.h
@@ -92,8 +92,33 @@ public:
const std::string& key,
const LLColor3& color,
const std::string& tool_tip = LLStringUtil::null);
-
- typedef std::map<LLWString, LLKeywordToken*> word_token_map_t;
+
+ // This class is here as a performance optimization.
+ // The word token map used to be defined as std::map<LLWString, LLKeywordToken*>.
+ // This worked, but caused a performance bottleneck due to memory allocation and string copies
+ // because it's not possible to search such a map without creating an LLWString.
+ // Using this class as the map index instead allows us to search using segments of an existing
+ // text run without copying them first, which greatly reduces overhead in LLKeywords::findSegments().
+ class WStringMapIndex
+ {
+ public:
+ // copy constructor
+ WStringMapIndex(const WStringMapIndex& other);
+ // constructor from a string (copies the string's data into the new object)
+ WStringMapIndex(const LLWString& str);
+ // constructor from pointer and length
+ // NOTE: does NOT copy data, caller must ensure that the lifetime of the pointer exceeds that of the new object!
+ WStringMapIndex(const llwchar *start, size_t length);
+ ~WStringMapIndex();
+ bool operator<(const WStringMapIndex &other) const;
+ private:
+ void copyData(const llwchar *start, size_t length);
+ const llwchar *mData;
+ size_t mLength;
+ bool mOwner;
+ };
+
+ typedef std::map<WStringMapIndex, LLKeywordToken*> word_token_map_t;
typedef word_token_map_t::const_iterator keyword_iterator_t;
keyword_iterator_t begin() const { return mWordTokenMap.begin(); }
keyword_iterator_t end() const { return mWordTokenMap.end(); }
diff --git a/indra/llui/lllayoutstack.cpp b/indra/llui/lllayoutstack.cpp
index 1aaba88c49..4512091371 100644
--- a/indra/llui/lllayoutstack.cpp
+++ b/indra/llui/lllayoutstack.cpp
@@ -49,9 +49,11 @@ static LLDefaultChildRegistry::Register<LLLayoutStack> register_layout_stack("la
//
struct LLLayoutStack::LayoutPanel
{
- LayoutPanel(LLPanel* panelp, ELayoutOrientation orientation, S32 min_width, S32 min_height, BOOL auto_resize, BOOL user_resize) : mPanel(panelp),
+ LayoutPanel(LLPanel* panelp, ELayoutOrientation orientation, S32 min_width, S32 min_height, S32 max_width, S32 max_height, BOOL auto_resize, BOOL user_resize) : mPanel(panelp),
mMinWidth(min_width),
mMinHeight(min_height),
+ mMaxWidth(max_width),
+ mMaxHeight(max_height),
mAutoResize(auto_resize),
mUserResize(user_resize),
mOrientation(orientation),
@@ -112,6 +114,11 @@ struct LLLayoutStack::LayoutPanel
LLPanel* mPanel;
S32 mMinWidth;
S32 mMinHeight;
+
+ // mMaxWidth & mMaxHeight are added to make configurable max width of the nearby chat bar. EXT-5589
+ // they are not processed by LLLayoutStack but they can be if necessary
+ S32 mMaxWidth;
+ S32 mMaxHeight;
BOOL mAutoResize;
BOOL mUserResize;
BOOL mCollapsed;
@@ -261,10 +268,14 @@ LLView* LLLayoutStack::fromXML(LLXMLNodePtr node, LLView *parent, LLXMLNodePtr o
{
const S32 DEFAULT_MIN_WIDTH = 0;
const S32 DEFAULT_MIN_HEIGHT = 0;
+ const S32 DEFAULT_MAX_WIDTH = S32_MAX;
+ const S32 DEFAULT_MAX_HEIGHT = S32_MAX;
const BOOL DEFAULT_AUTO_RESIZE = TRUE;
S32 min_width = DEFAULT_MIN_WIDTH;
S32 min_height = DEFAULT_MIN_HEIGHT;
+ S32 max_width = DEFAULT_MAX_WIDTH;
+ S32 max_height = DEFAULT_MAX_HEIGHT;
BOOL auto_resize = DEFAULT_AUTO_RESIZE;
LLXMLNodePtr output_child;
@@ -281,6 +292,10 @@ LLView* LLLayoutStack::fromXML(LLXMLNodePtr node, LLView *parent, LLXMLNodePtr o
DEFAULT_MIN_WIDTH, output_child);
get_attribute_s32_and_write(child_node, "min_height", &min_height,
DEFAULT_MIN_HEIGHT, output_child);
+ get_attribute_s32_and_write(child_node, "max_width", &max_width,
+ DEFAULT_MAX_WIDTH, output_child);
+ get_attribute_s32_and_write(child_node, "max_height", &max_height,
+ DEFAULT_MAX_HEIGHT, output_child);
get_attribute_bool_and_write(child_node, "auto_resize", &auto_resize,
DEFAULT_AUTO_RESIZE, output_child);
@@ -293,7 +308,7 @@ LLView* LLLayoutStack::fromXML(LLXMLNodePtr node, LLView *parent, LLXMLNodePtr o
if (panelp)
{
panelp->setFollowsNone();
- layout_stackp->addPanel(panelp, min_width, min_height, auto_resize, user_resize);
+ layout_stackp->addPanel(panelp, min_width, min_height, max_width, max_height, auto_resize, user_resize);
}
}
else
@@ -309,7 +324,7 @@ LLView* LLLayoutStack::fromXML(LLXMLNodePtr node, LLView *parent, LLXMLNodePtr o
if (new_child)
{
// put child in new embedded panel
- layout_stackp->addPanel(panelp, min_width, min_height, auto_resize, user_resize);
+ layout_stackp->addPanel(panelp, min_width, min_height, max_width, max_height, auto_resize, user_resize);
// resize panel to contain widget and move widget to be contained in panel
panelp->setRect(new_child->getRect());
new_child->setOrigin(0, 0);
@@ -359,14 +374,14 @@ S32 LLLayoutStack::getDefaultWidth(S32 cur_width)
return cur_width;
}
-void LLLayoutStack::addPanel(LLPanel* panel, S32 min_width, S32 min_height, BOOL auto_resize, BOOL user_resize, EAnimate animate, S32 index)
+void LLLayoutStack::addPanel(LLPanel* panel, S32 min_width, S32 min_height, S32 max_width, S32 max_height, BOOL auto_resize, BOOL user_resize, EAnimate animate, S32 index)
{
// panel starts off invisible (collapsed)
if (animate == ANIMATE)
{
panel->setVisible(FALSE);
}
- LayoutPanel* embedded_panel = new LayoutPanel(panel, mOrientation, min_width, min_height, auto_resize, user_resize);
+ LayoutPanel* embedded_panel = new LayoutPanel(panel, mOrientation, min_width, min_height, max_width, max_height, auto_resize, user_resize);
mPanels.insert(mPanels.begin() + llclamp(index, 0, (S32)mPanels.size()), embedded_panel);
@@ -437,6 +452,19 @@ bool LLLayoutStack::getPanelMinSize(const std::string& panel_name, S32* min_widt
return NULL != panel;
}
+bool LLLayoutStack::getPanelMaxSize(const std::string& panel_name, S32* max_widthp, S32* max_heightp)
+{
+ LayoutPanel* panel = findEmbeddedPanelByName(panel_name);
+
+ if (panel)
+ {
+ if (max_widthp) *max_widthp = panel->mMaxWidth;
+ if (max_heightp) *max_heightp = panel->mMaxHeight;
+ }
+
+ return NULL != panel;
+}
+
static LLFastTimer::DeclareTimer FTM_UPDATE_LAYOUT("Update LayoutStacks");
void LLLayoutStack::updateLayout(BOOL force_resize)
{
@@ -816,7 +844,10 @@ void LLLayoutStack::calcMinExtents()
//static
void LLLayoutStack::updateClass()
{
- for (LLLayoutStack::instance_iter it = beginInstances(); it != endInstances(); ++it)
+ LLInstanceTrackerScopedGuard guard;
+ for (LLLayoutStack::instance_iter it = guard.beginInstances();
+ it != guard.endInstances();
+ ++it)
{
it->updateLayout();
}
diff --git a/indra/llui/lllayoutstack.h b/indra/llui/lllayoutstack.h
index c4f10038f8..e454454fe2 100644
--- a/indra/llui/lllayoutstack.h
+++ b/indra/llui/lllayoutstack.h
@@ -74,7 +74,7 @@ public:
ANIMATE
} EAnimate;
- void addPanel(LLPanel* panel, S32 min_width, S32 min_height, BOOL auto_resize, BOOL user_resize, EAnimate animate = NO_ANIMATE, S32 index = S32_MAX);
+ void addPanel(LLPanel* panel, S32 min_width, S32 min_height, S32 max_width, S32 max_height, BOOL auto_resize, BOOL user_resize, EAnimate animate = NO_ANIMATE, S32 index = S32_MAX);
void removePanel(LLPanel* panel);
void collapsePanel(LLPanel* panel, BOOL collapsed = TRUE);
S32 getNumPanels() { return mPanels.size(); }
@@ -89,6 +89,14 @@ public:
* @returns true if specified by panel_name internal panel exists, false otherwise.
*/
bool getPanelMinSize(const std::string& panel_name, S32* min_widthp, S32* min_heightp);
+
+ /**
+ * Gets maximal width and/or height of the specified by name panel.
+ *
+ * If it is necessary to get only the one dimension pass NULL for another one.
+ * @returns true if specified by panel_name internal panel exists, false otherwise.
+ */
+ bool getPanelMaxSize(const std::string& panel_name, S32* max_width, S32* max_height);
void updateLayout(BOOL force_resize = FALSE);
diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp
index 73e4d126f3..45f9de8e8d 100644
--- a/indra/llui/lllineeditor.cpp
+++ b/indra/llui/lllineeditor.cpp
@@ -55,6 +55,7 @@
#include "llui.h"
#include "lluictrlfactory.h"
#include "llclipboard.h"
+#include "llmenugl.h"
//
// Imported globals
@@ -70,6 +71,8 @@ const S32 SCROLL_INCREMENT_DEL = 4; // make space for baskspacing
const F32 AUTO_SCROLL_TIME = 0.05f;
const F32 TRIPLE_CLICK_INTERVAL = 0.3f; // delay between double and triple click. *TODO: make this equal to the double click interval?
+const std::string PASSWORD_ASTERISK( "\xE2\x80\xA2" ); // U+2022 BULLET
+
static LLDefaultChildRegistry::Register<LLLineEditor> r1("line_editor");
// Compiler optimization, generate extern template
@@ -80,19 +83,6 @@ template class LLLineEditor* LLView::getChild<class LLLineEditor>(
// Member functions
//
-void LLLineEditor::PrevalidateNamedFuncs::declareValues()
-{
- declare("ascii", LLLineEditor::prevalidateASCII);
- declare("float", LLLineEditor::prevalidateFloat);
- declare("int", LLLineEditor::prevalidateInt);
- declare("positive_s32", LLLineEditor::prevalidatePositiveS32);
- declare("non_negative_s32", LLLineEditor::prevalidateNonNegativeS32);
- declare("alpha_num", LLLineEditor::prevalidateAlphaNum);
- declare("alpha_num_space", LLLineEditor::prevalidateAlphaNumSpace);
- declare("ascii_printable_no_pipe", LLLineEditor::prevalidateASCIIPrintableNoPipe);
- declare("ascii_printable_no_space", LLLineEditor::prevalidateASCIIPrintableNoSpace);
-}
-
LLLineEditor::Params::Params()
: max_length_bytes("max_length", 254),
keystroke_callback("keystroke_callback"),
@@ -101,7 +91,6 @@ LLLineEditor::Params::Params()
background_image_disabled("background_image_disabled"),
background_image_focused("background_image_focused"),
select_on_focus("select_on_focus", false),
- handle_edit_keys_directly("handle_edit_keys_directly", false),
revert_on_esc("revert_on_esc", true),
commit_on_focus_lost("commit_on_focus_lost", true),
ignore_tab("ignore_tab", true),
@@ -146,7 +135,6 @@ LLLineEditor::LLLineEditor(const LLLineEditor::Params& p)
mIgnoreArrowKeys( FALSE ),
mIgnoreTab( p.ignore_tab ),
mDrawAsterixes( FALSE ),
- mHandleEditKeysDirectly(p.handle_edit_keys_directly),
mSelectAllonFocusReceived( p.select_on_focus ),
mPassDelete(FALSE),
mReadOnly(FALSE),
@@ -162,7 +150,8 @@ LLLineEditor::LLLineEditor(const LLLineEditor::Params& p)
mTentativeFgColor(p.text_tentative_color()),
mHighlightColor(p.highlight_color()),
mPreeditBgColor(p.preedit_bg_color()),
- mGLFont(p.font)
+ mGLFont(p.font),
+ mContextMenuHandle()
{
llassert( mMaxLengthBytes > 0 );
@@ -189,18 +178,20 @@ LLLineEditor::LLLineEditor(const LLLineEditor::Params& p)
setCursor(mText.length());
setPrevalidate(p.prevalidate_callback());
+
+ LLContextMenu* menu = LLUICtrlFactory::instance().createFromFile<LLContextMenu>
+ ("menu_text_editor.xml",
+ LLMenuGL::sMenuContainer,
+ LLMenuHolderGL::child_registry_t::instance());
+ setContextMenu(menu);
}
LLLineEditor::~LLLineEditor()
{
mCommitOnFocusLost = FALSE;
+ // calls onCommit() while LLLineEditor still valid
gFocusMgr.releaseFocusIfNeeded( this );
-
- if( gEditMenuHandler == this )
- {
- gEditMenuHandler = NULL;
- }
}
@@ -401,7 +392,7 @@ void LLLineEditor::setCursorAtLocalPos( S32 local_mouse_x )
{
for (S32 i = 0; i < mText.length(); i++)
{
- asterix_text += '*';
+ asterix_text += utf8str_to_wstring(PASSWORD_ASTERISK);
}
wtext = asterix_text.c_str();
}
@@ -420,12 +411,16 @@ void LLLineEditor::setCursor( S32 pos )
S32 old_cursor_pos = getCursor();
mCursorPos = llclamp( pos, 0, mText.length());
+ // position of end of next character after cursor
S32 pixels_after_scroll = findPixelNearestPos();
if( pixels_after_scroll > mTextRightEdge )
{
S32 width_chars_to_left = mGLFont->getWidth(mText.getWString().c_str(), 0, mScrollHPos);
S32 last_visible_char = mGLFont->maxDrawableChars(mText.getWString().c_str(), llmax(0.f, (F32)(mTextRightEdge - mTextLeftEdge + width_chars_to_left)));
- S32 min_scroll = mGLFont->firstDrawableChar(mText.getWString().c_str(), (F32)(mTextRightEdge - mTextLeftEdge), mText.length(), getCursor());
+ // character immediately to left of cursor should be last one visible (SCROLL_INCREMENT_ADD will scroll in more characters)
+ // or first character if cursor is at beginning
+ S32 new_last_visible_char = llmax(0, getCursor() - 1);
+ S32 min_scroll = mGLFont->firstDrawableChar(mText.getWString().c_str(), (F32)(mTextRightEdge - mTextLeftEdge), mText.length(), new_last_visible_char);
if (old_cursor_pos == last_visible_char)
{
mScrollHPos = llmin(mText.length(), llmax(min_scroll, mScrollHPos + SCROLL_INCREMENT_ADD));
@@ -496,6 +491,7 @@ void LLLineEditor::selectAll()
setCursor(mSelectionEnd);
//mScrollHPos = 0;
mIsSelecting = TRUE;
+ updatePrimary();
}
@@ -661,6 +657,16 @@ BOOL LLLineEditor::handleMiddleMouseDown(S32 x, S32 y, MASK mask)
return TRUE;
}
+BOOL LLLineEditor::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+ setFocus(TRUE);
+ if (!LLUICtrl::handleRightMouseDown(x, y, mask))
+ {
+ showContextMenu(x, y);
+ }
+ return TRUE;
+}
+
BOOL LLLineEditor::handleHover(S32 x, S32 y, MASK mask)
{
BOOL handled = FALSE;
@@ -777,7 +783,7 @@ void LLLineEditor::removeChar()
}
else
{
- reportBadKeystroke();
+ LLUI::reportBadKeystroke();
}
}
@@ -816,7 +822,7 @@ void LLLineEditor::addChar(const llwchar uni_char)
}
else
{
- reportBadKeystroke();
+ LLUI::reportBadKeystroke();
}
getWindow()->hideCursorUntilMouseMove();
@@ -905,7 +911,7 @@ BOOL LLLineEditor::handleSelectionKey(KEY key, MASK mask)
}
else
{
- reportBadKeystroke();
+ LLUI::reportBadKeystroke();
}
break;
@@ -921,7 +927,7 @@ BOOL LLLineEditor::handleSelectionKey(KEY key, MASK mask)
}
else
{
- reportBadKeystroke();
+ LLUI::reportBadKeystroke();
}
break;
@@ -947,22 +953,6 @@ BOOL LLLineEditor::handleSelectionKey(KEY key, MASK mask)
}
}
- if (!handled && mHandleEditKeysDirectly)
- {
- if( (MASK_CONTROL & mask) && ('A' == key) )
- {
- if( canSelectAll() )
- {
- selectAll();
- }
- else
- {
- reportBadKeystroke();
- }
- handled = TRUE;
- }
- }
-
if(handled)
{
// take selection to 'primary' clipboard
@@ -1009,7 +999,7 @@ void LLLineEditor::cut()
if( need_to_rollback )
{
rollback.doRollback( this );
- reportBadKeystroke();
+ LLUI::reportBadKeystroke();
}
else
if( mKeystrokeCallback )
@@ -1118,7 +1108,7 @@ void LLLineEditor::pasteHelper(bool is_primary)
}
// Truncate the clean string at the limit of what will fit
clean_string = clean_string.substr(0, wchars_that_fit);
- reportBadKeystroke();
+ LLUI::reportBadKeystroke();
}
mText.insert(getCursor(), clean_string);
@@ -1130,7 +1120,7 @@ void LLLineEditor::pasteHelper(bool is_primary)
if( need_to_rollback )
{
rollback.doRollback( this );
- reportBadKeystroke();
+ LLUI::reportBadKeystroke();
}
else
if( mKeystrokeCallback )
@@ -1195,7 +1185,7 @@ BOOL LLLineEditor::handleSpecialKey(KEY key, MASK mask)
}
else
{
- reportBadKeystroke();
+ LLUI::reportBadKeystroke();
}
}
handled = TRUE;
@@ -1244,7 +1234,7 @@ BOOL LLLineEditor::handleSpecialKey(KEY key, MASK mask)
}
else
{
- reportBadKeystroke();
+ LLUI::reportBadKeystroke();
}
handled = TRUE;
}
@@ -1271,7 +1261,7 @@ BOOL LLLineEditor::handleSpecialKey(KEY key, MASK mask)
}
else
{
- reportBadKeystroke();
+ LLUI::reportBadKeystroke();
}
handled = TRUE;
}
@@ -1288,7 +1278,7 @@ BOOL LLLineEditor::handleSpecialKey(KEY key, MASK mask)
}
else
{
- reportBadKeystroke();
+ LLUI::reportBadKeystroke();
}
handled = TRUE;
}
@@ -1305,7 +1295,7 @@ BOOL LLLineEditor::handleSpecialKey(KEY key, MASK mask)
}
else
{
- reportBadKeystroke();
+ LLUI::reportBadKeystroke();
}
handled = TRUE;
}
@@ -1328,64 +1318,6 @@ BOOL LLLineEditor::handleSpecialKey(KEY key, MASK mask)
break;
}
- if( !handled && mHandleEditKeysDirectly )
- {
- // Standard edit keys (Ctrl-X, Delete, etc,) are handled here instead of routed by the menu system.
- if( KEY_DELETE == key )
- {
- if( canDoDelete() )
- {
- doDelete();
- }
- else
- {
- reportBadKeystroke();
- }
- handled = TRUE;
- }
- else
- if( MASK_CONTROL & mask )
- {
- if( 'C' == key )
- {
- if( canCopy() )
- {
- copy();
- }
- else
- {
- reportBadKeystroke();
- }
- handled = TRUE;
- }
- else
- if( 'V' == key )
- {
- if( canPaste() )
- {
- paste();
- }
- else
- {
- reportBadKeystroke();
- }
- handled = TRUE;
- }
- else
- if( 'X' == key )
- {
- if( canCut() )
- {
- cut();
- }
- else
- {
- reportBadKeystroke();
- }
- handled = TRUE;
- }
- }
- }
return handled;
}
@@ -1440,7 +1372,7 @@ BOOL LLLineEditor::handleKeyHere(KEY key, MASK mask )
{
rollback.doRollback(this);
- reportBadKeystroke();
+ LLUI::reportBadKeystroke();
}
// Notify owner if requested
@@ -1488,7 +1420,7 @@ BOOL LLLineEditor::handleUnicodeCharHere(llwchar uni_char)
{
rollback.doRollback( this );
- reportBadKeystroke();
+ LLUI::reportBadKeystroke();
}
// Notify owner if requested
@@ -1508,7 +1440,7 @@ BOOL LLLineEditor::handleUnicodeCharHere(llwchar uni_char)
BOOL LLLineEditor::canDoDelete() const
{
- return ( !mReadOnly && (!mPassDelete || (hasSelection() || (getCursor() < mText.length()))) );
+ return ( !mReadOnly && mText.length() > 0 && (!mPassDelete || (hasSelection() || (getCursor() < mText.length()))) );
}
void LLLineEditor::doDelete()
@@ -1533,7 +1465,7 @@ void LLLineEditor::doDelete()
if( need_to_rollback )
{
rollback.doRollback( this );
- reportBadKeystroke();
+ LLUI::reportBadKeystroke();
}
else
{
@@ -1599,7 +1531,7 @@ void LLLineEditor::draw()
std::string text;
for (S32 i = 0; i < mText.length(); i++)
{
- text += '*';
+ text += PASSWORD_ASTERISK;
}
mText = text;
}
@@ -1868,11 +1800,6 @@ S32 LLLineEditor::findPixelNearestPos(const S32 cursor_offset) const
return result;
}
-void LLLineEditor::reportBadKeystroke()
-{
- make_ui_sound("UISndBadKeystroke");
-}
-
//virtual
void LLLineEditor::clear()
{
@@ -1960,51 +1887,12 @@ void LLLineEditor::setRect(const LLRect& rect)
}
}
-void LLLineEditor::setPrevalidate(LLLinePrevalidateFunc func)
+void LLLineEditor::setPrevalidate(LLTextValidate::validate_func_t func)
{
mPrevalidateFunc = func;
updateAllowingLanguageInput();
}
-// Limits what characters can be used to [1234567890.-] with [-] only valid in the first position.
-// Does NOT ensure that the string is a well-formed number--that's the job of post-validation--for
-// the simple reasons that intermediate states may be invalid even if the final result is valid.
-//
-// static
-BOOL LLLineEditor::prevalidateFloat(const LLWString &str)
-{
- LLLocale locale(LLLocale::USER_LOCALE);
-
- BOOL success = TRUE;
- LLWString trimmed = str;
- LLWStringUtil::trim(trimmed);
- S32 len = trimmed.length();
- if( 0 < len )
- {
- // May be a comma or period, depending on the locale
- llwchar decimal_point = (llwchar)LLResMgr::getInstance()->getDecimalPoint();
-
- S32 i = 0;
-
- // First character can be a negative sign
- if( '-' == trimmed[0] )
- {
- i++;
- }
-
- for( ; i < len; i++ )
- {
- if( (decimal_point != trimmed[i] ) && !LLStringOps::isDigit( trimmed[i] ) )
- {
- success = FALSE;
- break;
- }
- }
- }
-
- return success;
-}
-
// static
BOOL LLLineEditor::postvalidateFloat(const std::string &str)
{
@@ -2064,223 +1952,6 @@ BOOL LLLineEditor::postvalidateFloat(const std::string &str)
return success;
}
-// Limits what characters can be used to [1234567890-] with [-] only valid in the first position.
-// Does NOT ensure that the string is a well-formed number--that's the job of post-validation--for
-// the simple reasons that intermediate states may be invalid even if the final result is valid.
-//
-// static
-BOOL LLLineEditor::prevalidateInt(const LLWString &str)
-{
- LLLocale locale(LLLocale::USER_LOCALE);
-
- BOOL success = TRUE;
- LLWString trimmed = str;
- LLWStringUtil::trim(trimmed);
- S32 len = trimmed.length();
- if( 0 < len )
- {
- S32 i = 0;
-
- // First character can be a negative sign
- if( '-' == trimmed[0] )
- {
- i++;
- }
-
- for( ; i < len; i++ )
- {
- if( !LLStringOps::isDigit( trimmed[i] ) )
- {
- success = FALSE;
- break;
- }
- }
- }
-
- return success;
-}
-
-// static
-BOOL LLLineEditor::prevalidatePositiveS32(const LLWString &str)
-{
- LLLocale locale(LLLocale::USER_LOCALE);
-
- LLWString trimmed = str;
- LLWStringUtil::trim(trimmed);
- S32 len = trimmed.length();
- BOOL success = TRUE;
- if(0 < len)
- {
- if(('-' == trimmed[0]) || ('0' == trimmed[0]))
- {
- success = FALSE;
- }
- S32 i = 0;
- while(success && (i < len))
- {
- if(!LLStringOps::isDigit(trimmed[i++]))
- {
- success = FALSE;
- }
- }
- }
- if (success)
- {
- S32 val = strtol(wstring_to_utf8str(trimmed).c_str(), NULL, 10);
- if (val <= 0)
- {
- success = FALSE;
- }
- }
- return success;
-}
-
-BOOL LLLineEditor::prevalidateNonNegativeS32(const LLWString &str)
-{
- LLLocale locale(LLLocale::USER_LOCALE);
-
- LLWString trimmed = str;
- LLWStringUtil::trim(trimmed);
- S32 len = trimmed.length();
- BOOL success = TRUE;
- if(0 < len)
- {
- if('-' == trimmed[0])
- {
- success = FALSE;
- }
- S32 i = 0;
- while(success && (i < len))
- {
- if(!LLStringOps::isDigit(trimmed[i++]))
- {
- success = FALSE;
- }
- }
- }
- if (success)
- {
- S32 val = strtol(wstring_to_utf8str(trimmed).c_str(), NULL, 10);
- if (val < 0)
- {
- success = FALSE;
- }
- }
- return success;
-}
-
-BOOL LLLineEditor::prevalidateAlphaNum(const LLWString &str)
-{
- LLLocale locale(LLLocale::USER_LOCALE);
-
- BOOL rv = TRUE;
- S32 len = str.length();
- if(len == 0) return rv;
- while(len--)
- {
- if( !LLStringOps::isAlnum((char)str[len]) )
- {
- rv = FALSE;
- break;
- }
- }
- return rv;
-}
-
-// static
-BOOL LLLineEditor::prevalidateAlphaNumSpace(const LLWString &str)
-{
- LLLocale locale(LLLocale::USER_LOCALE);
-
- BOOL rv = TRUE;
- S32 len = str.length();
- if(len == 0) return rv;
- while(len--)
- {
- if(!(LLStringOps::isAlnum((char)str[len]) || (' ' == str[len])))
- {
- rv = FALSE;
- break;
- }
- }
- return rv;
-}
-
-// Used for most names of things stored on the server, due to old file-formats
-// that used the pipe (|) for multiline text storage. Examples include
-// inventory item names, parcel names, object names, etc.
-// static
-BOOL LLLineEditor::prevalidateASCIIPrintableNoPipe(const LLWString &str)
-{
- BOOL rv = TRUE;
- S32 len = str.length();
- if(len == 0) return rv;
- while(len--)
- {
- llwchar wc = str[len];
- if (wc < 0x20
- || wc > 0x7f
- || wc == '|')
- {
- rv = FALSE;
- break;
- }
- if(!(wc == ' '
- || LLStringOps::isAlnum((char)wc)
- || LLStringOps::isPunct((char)wc) ) )
- {
- rv = FALSE;
- break;
- }
- }
- return rv;
-}
-
-
-// Used for avatar names
-// static
-BOOL LLLineEditor::prevalidateASCIIPrintableNoSpace(const LLWString &str)
-{
- BOOL rv = TRUE;
- S32 len = str.length();
- if(len == 0) return rv;
- while(len--)
- {
- llwchar wc = str[len];
- if (wc < 0x20
- || wc > 0x7f
- || LLStringOps::isSpace(wc))
- {
- rv = FALSE;
- break;
- }
- if( !(LLStringOps::isAlnum((char)str[len]) ||
- LLStringOps::isPunct((char)str[len]) ) )
- {
- rv = FALSE;
- break;
- }
- }
- return rv;
-}
-
-
-// static
-BOOL LLLineEditor::prevalidateASCII(const LLWString &str)
-{
- BOOL rv = TRUE;
- S32 len = str.length();
- while(len--)
- {
- if (str[len] < 0x20 || str[len] > 0x7f)
- {
- rv = FALSE;
- break;
- }
- }
- return rv;
-}
-
void LLLineEditor::onMouseCaptureLost()
{
endSelection();
@@ -2558,3 +2229,25 @@ LLWString LLLineEditor::getConvertedText() const
}
return text;
}
+
+void LLLineEditor::showContextMenu(S32 x, S32 y)
+{
+ LLContextMenu* menu = static_cast<LLContextMenu*>(mContextMenuHandle.get());
+
+ if (menu)
+ {
+ gEditMenuHandler = this;
+
+ S32 screen_x, screen_y;
+ localPointToScreen(x, y, &screen_x, &screen_y);
+ menu->show(screen_x, screen_y);
+ }
+}
+
+void LLLineEditor::setContextMenu(LLContextMenu* new_context_menu)
+{
+ if (new_context_menu)
+ mContextMenuHandle = new_context_menu->getHandle();
+ else
+ mContextMenuHandle.markDead();
+}
diff --git a/indra/llui/lllineeditor.h b/indra/llui/lllineeditor.h
index 49e9539b16..9489e723e3 100644
--- a/indra/llui/lllineeditor.h
+++ b/indra/llui/lllineeditor.h
@@ -51,26 +51,18 @@
#include "llviewborder.h"
#include "llpreeditor.h"
-#include <boost/function.hpp>
+#include "lltextvalidate.h"
class LLFontGL;
class LLLineEditorRollback;
class LLButton;
-
-typedef boost::function<BOOL (const LLWString &wstr)> LLLinePrevalidateFunc;
+class LLContextMenu;
class LLLineEditor
: public LLUICtrl, public LLEditMenuHandler, protected LLPreeditor
{
public:
- struct PrevalidateNamedFuncs
- : public LLInitParam::TypeValuesHelper<LLLinePrevalidateFunc, PrevalidateNamedFuncs>
-
- {
- static void declareValues();
- };
-
typedef boost::function<void (LLLineEditor* caller)> keystroke_callback_t;
struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
@@ -80,7 +72,7 @@ public:
Optional<keystroke_callback_t> keystroke_callback;
- Optional<LLLinePrevalidateFunc, PrevalidateNamedFuncs> prevalidate_callback;
+ Optional<LLTextValidate::validate_func_t, LLTextValidate::ValidateTextNamedFuncs> prevalidate_callback;
Optional<LLViewBorder::Params> border;
@@ -89,7 +81,6 @@ public:
background_image_focused;
Optional<bool> select_on_focus,
- handle_edit_keys_directly,
revert_on_esc,
commit_on_focus_lost,
ignore_tab;
@@ -113,6 +104,7 @@ protected:
LLLineEditor(const Params&);
friend class LLUICtrlFactory;
friend class LLFloaterEditUI;
+ void showContextMenu(S32 x, S32 y);
public:
virtual ~LLLineEditor();
@@ -122,6 +114,7 @@ public:
/*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask);
/*virtual*/ BOOL handleDoubleClick(S32 x,S32 y,MASK mask);
/*virtual*/ BOOL handleMiddleMouseDown(S32 x,S32 y,MASK mask);
+ /*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask);
/*virtual*/ BOOL handleKeyHere(KEY key, MASK mask );
/*virtual*/ BOOL handleUnicodeCharHere(llwchar uni_char);
/*virtual*/ void onMouseCaptureLost();
@@ -204,6 +197,8 @@ public:
const LLColor4& getReadOnlyFgColor() const { return mReadOnlyFgColor.get(); }
const LLColor4& getTentativeFgColor() const { return mTentativeFgColor.get(); }
+ const LLFontGL* getFont() const { return mGLFont; }
+
void setIgnoreArrowKeys(BOOL b) { mIgnoreArrowKeys = b; }
void setIgnoreTab(BOOL b) { mIgnoreTab = b; }
void setPassDelete(BOOL b) { mPassDelete = b; }
@@ -219,7 +214,6 @@ public:
void extendSelection(S32 new_cursor_pos);
void deleteSelection();
- void setHandleEditKeysDirectly( BOOL b ) { mHandleEditKeysDirectly = b; }
void setSelectAllonFocusReceived(BOOL b);
typedef boost::function<void (LLLineEditor* caller, void* user_data)> callback_t;
@@ -231,17 +225,7 @@ public:
void setTextPadding(S32 left, S32 right);
// Prevalidation controls which keystrokes can affect the editor
- void setPrevalidate( LLLinePrevalidateFunc func );
- static BOOL prevalidateFloat(const LLWString &str );
- static BOOL prevalidateInt(const LLWString &str );
- static BOOL prevalidatePositiveS32(const LLWString &str);
- static BOOL prevalidateNonNegativeS32(const LLWString &str);
- static BOOL prevalidateAlphaNum(const LLWString &str );
- static BOOL prevalidateAlphaNumSpace(const LLWString &str );
- static BOOL prevalidateASCIIPrintableNoPipe(const LLWString &str);
- static BOOL prevalidateASCIIPrintableNoSpace(const LLWString &str);
- static BOOL prevalidateASCII(const LLWString &str);
-
+ void setPrevalidate( LLTextValidate::validate_func_t func );
static BOOL postvalidateFloat(const std::string &str);
// line history support:
@@ -249,7 +233,9 @@ public:
void updateHistory(); // stores current line in history
void setReplaceNewlinesWithSpaces(BOOL replace);
-
+
+ void setContextMenu(LLContextMenu* new_context_menu);
+
private:
// private helper methods
@@ -259,7 +245,6 @@ private:
void addChar(const llwchar c);
void setCursorAtLocalPos(S32 local_mouse_x);
S32 findPixelNearestPos(S32 cursor_offset = 0) const;
- void reportBadKeystroke();
BOOL handleSpecialKey(KEY key, MASK mask);
BOOL handleSelectionKey(KEY key, MASK mask);
BOOL handleControlKey(KEY key, MASK mask);
@@ -319,7 +304,7 @@ protected:
S32 mLastSelectionStart;
S32 mLastSelectionEnd;
- LLLinePrevalidateFunc mPrevalidateFunc;
+ LLTextValidate::validate_func_t mPrevalidateFunc;
LLFrameTimer mKeystrokeTimer;
LLTimer mTripleClickTimer;
@@ -337,7 +322,6 @@ protected:
BOOL mIgnoreTab;
BOOL mDrawAsterixes;
- BOOL mHandleEditKeysDirectly; // If true, the standard edit keys (Ctrl-X, Delete, etc,) are handled here instead of routed by the menu system
BOOL mSelectAllonFocusReceived;
BOOL mPassDelete;
@@ -348,6 +332,8 @@ protected:
std::vector<S32> mPreeditPositions;
LLPreeditor::standouts_t mPreeditStandouts;
+ LLHandle<LLView> mContextMenuHandle;
+
private:
// Instances that by default point to the statics but can be overidden in XML.
LLPointer<LLUIImage> mBgImage;
diff --git a/indra/llui/llloadingindicator.cpp b/indra/llui/llloadingindicator.cpp
new file mode 100644
index 0000000000..f8b029e19c
--- /dev/null
+++ b/indra/llui/llloadingindicator.cpp
@@ -0,0 +1,133 @@
+/**
+ * @file llloadingindicator.cpp
+ * @brief Perpetual loading indicator
+ *
+ * $LicenseInfo:firstyear=2010&license=viewergpl$
+ *
+ * Copyright (c) 2010, 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 "llloadingindicator.h"
+
+// Linden library includes
+#include "llsingleton.h"
+
+// Project includes
+#include "lluictrlfactory.h"
+#include "lluiimage.h"
+
+static LLDefaultChildRegistry::Register<LLLoadingIndicator> r("loading_indicator");
+
+///////////////////////////////////////////////////////////////////////////////
+// LLLoadingIndicator::Data class
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Pre-loaded images shared by all instances of the widget
+ */
+class LLLoadingIndicator::Data: public LLSingleton<LLLoadingIndicator::Data>
+{
+public:
+ /*virtual*/ void initSingleton(); // from LLSingleton
+
+ LLPointer<LLUIImage> getNextImage(S8& idx) const;
+ U8 getImagesCount() const { return NIMAGES; }
+private:
+
+ static const U8 NIMAGES = 12;
+ LLPointer<LLUIImage> mImages[NIMAGES];
+};
+
+// virtual
+// Called right after the instance gets constructed.
+void LLLoadingIndicator::Data::initSingleton()
+{
+ // Load images.
+ for (U8 i = 0; i < NIMAGES; ++i)
+ {
+ std::string img_name = llformat("Progress_%d", i+1);
+ mImages[i] = LLUI::getUIImage(img_name, 0);
+ llassert(mImages[i]);
+ }
+}
+
+LLPointer<LLUIImage> LLLoadingIndicator::Data::getNextImage(S8& idx) const
+{
+ // Calculate next index, performing array bounds checking.
+ idx = (idx >= NIMAGES || idx < 0) ? 0 : (idx + 1) % NIMAGES;
+ return mImages[idx];
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// LLLoadingIndicator class
+///////////////////////////////////////////////////////////////////////////////
+
+LLLoadingIndicator::LLLoadingIndicator(const Params& p)
+: LLUICtrl(p)
+ , mRotationsPerSec(p.rotations_per_sec > 0 ? p.rotations_per_sec : 1.0f)
+ , mCurImageIdx(-1)
+{
+ // Select initial image.
+ mCurImagep = Data::instance().getNextImage(mCurImageIdx);
+
+ // Start timer for switching images.
+ start();
+}
+
+void LLLoadingIndicator::draw()
+{
+ // Time to switch to the next image?
+ if (mImageSwitchTimer.getStarted() && mImageSwitchTimer.hasExpired())
+ {
+ // Switch to the next image.
+ mCurImagep = Data::instance().getNextImage(mCurImageIdx);
+
+ // Restart timer.
+ start();
+ }
+
+ // Draw current image.
+ if( mCurImagep.notNull() )
+ {
+ mCurImagep->draw(getLocalRect(), LLColor4::white % getDrawContext().mAlpha);
+ }
+
+ LLUICtrl::draw();
+}
+
+void LLLoadingIndicator::stop()
+{
+ mImageSwitchTimer.stop();
+}
+
+void LLLoadingIndicator::start()
+{
+ mImageSwitchTimer.start();
+ F32 period = 1.0f / (Data::instance().getImagesCount() * mRotationsPerSec);
+ mImageSwitchTimer.setTimerExpirySec(period);
+}
diff --git a/indra/llui/llloadingindicator.h b/indra/llui/llloadingindicator.h
new file mode 100644
index 0000000000..32dd1fead8
--- /dev/null
+++ b/indra/llui/llloadingindicator.h
@@ -0,0 +1,93 @@
+/**
+ * @file llloadingindicator.h
+ * @brief Perpetual loading indicator
+ *
+ * $LicenseInfo:firstyear=2010&license=viewergpl$
+ *
+ * Copyright (c) 2010, 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_LLLOADINGINDICATOR_H
+#define LL_LLLOADINGINDICATOR_H
+
+#include "lluictrl.h"
+
+///////////////////////////////////////////////////////////////////////////////
+// class LLLoadingIndicator
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Perpetual loading indicator (a la MacOSX or YouTube)
+ *
+ * Number of rotations per second can be overriden
+ * with the "roations_per_sec" parameter.
+ *
+ * Can start/stop spinning.
+ *
+ * @see start()
+ * @see stop()
+ */
+class LLLoadingIndicator
+: public LLUICtrl
+{
+ LOG_CLASS(LLLoadingIndicator);
+public:
+ struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
+ {
+ Optional<F32> rotations_per_sec;
+ Params()
+ : rotations_per_sec("rotations_per_sec", 1.0f)
+ {}
+ };
+
+ virtual ~LLLoadingIndicator() {}
+
+ // llview overrides
+ virtual void draw();
+
+ /**
+ * Stop spinning.
+ */
+ void stop();
+
+ /**
+ * Start spinning.
+ */
+ void start();
+
+private:
+ LLLoadingIndicator(const Params&);
+ friend class LLUICtrlFactory;
+
+ class Data;
+
+ F32 mRotationsPerSec;
+ S8 mCurImageIdx;
+ LLPointer<LLUIImage> mCurImagep;
+ LLFrameTimer mImageSwitchTimer;
+};
+
+#endif // LL_LLLOADINGINDICATOR_H
diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp
index c172a2b714..e0e86ae228 100644
--- a/indra/llui/llmenugl.cpp
+++ b/indra/llui/llmenugl.cpp
@@ -657,11 +657,38 @@ LLMenuItemVerticalSeparatorGL::LLMenuItemVerticalSeparatorGL( void )
// Class LLMenuItemTearOffGL
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
LLMenuItemTearOffGL::LLMenuItemTearOffGL(const LLMenuItemTearOffGL::Params& p)
-: LLMenuItemGL(p),
- mParentHandle(p.parent_floater_handle)
+: LLMenuItemGL(p)
{
}
+// Returns the first floater ancestor if there is one
+LLFloater* LLMenuItemTearOffGL::getParentFloater()
+{
+ LLView* parent_view = getMenu();
+
+ while (parent_view)
+ {
+ if (dynamic_cast<LLFloater*>(parent_view))
+ {
+ return dynamic_cast<LLFloater*>(parent_view);
+ }
+
+ bool parent_is_menu = dynamic_cast<LLMenuGL*>(parent_view) && !dynamic_cast<LLMenuBarGL*>(parent_view);
+
+ if (parent_is_menu)
+ {
+ // use menu parent
+ parent_view = dynamic_cast<LLMenuGL*>(parent_view)->getParentMenuItem();
+ }
+ else
+ {
+ // just use regular view parent
+ parent_view = parent_view->getParent();
+ }
+ }
+
+ return NULL;
+}
void LLMenuItemTearOffGL::onCommit()
{
@@ -680,7 +707,7 @@ void LLMenuItemTearOffGL::onCommit()
getMenu()->needsArrange();
- LLFloater* parent_floater = mParentHandle.get();
+ LLFloater* parent_floater = getParentFloater();
LLFloater* tear_off_menu = LLTearOffMenu::create(getMenu());
if (tear_off_menu)
@@ -1651,6 +1678,7 @@ LLMenuGL::LLMenuGL(const LLMenuGL::Params& p)
mBackgroundColor( p.bg_color() ),
mBgVisible( p.bg_visible ),
mDropShadowed( p.drop_shadow ),
+ mHasSelection(false),
mHorizontalLayout( p.horizontal_layout ),
mScrollable(mHorizontalLayout ? FALSE : p.scrollable), // Scrolling is supported only for vertical layout
mMaxScrollableItems(p.max_scrollable_items),
@@ -1670,7 +1698,6 @@ LLMenuGL::LLMenuGL(const LLMenuGL::Params& p)
mSpilloverMenu(NULL),
mJumpKey(p.jump_key),
mCreateJumpKeys(p.create_jump_keys),
- mParentFloaterHandle(p.parent_floater),
mNeedsArrange(FALSE),
mShortcutPad(p.shortcut_pad)
{
@@ -1698,7 +1725,7 @@ LLMenuGL::LLMenuGL(const LLMenuGL::Params& p)
void LLMenuGL::initFromParams(const LLMenuGL::Params& p)
{
LLUICtrl::initFromParams(p);
- setCanTearOff(p.can_tear_off, p.parent_floater);
+ setCanTearOff(p.can_tear_off);
}
// Destroys the object
@@ -1710,12 +1737,11 @@ LLMenuGL::~LLMenuGL( void )
mJumpKeys.clear();
}
-void LLMenuGL::setCanTearOff(BOOL tear_off, LLHandle<LLFloater> parent_floater_handle )
+void LLMenuGL::setCanTearOff(BOOL tear_off)
{
if (tear_off && mTearOffItem == NULL)
{
LLMenuItemTearOffGL::Params p;
- p.parent_floater_handle = parent_floater_handle;
mTearOffItem = LLUICtrlFactory::create<LLMenuItemTearOffGL>(p);
addChildInBack(mTearOffItem);
}
@@ -1875,17 +1901,21 @@ void LLMenuGL::scrollItemsDown()
item_list_t::iterator next_item_iter;
- for (next_item_iter = ++cur_item_iter; next_item_iter != mItems.end(); next_item_iter++)
+ if (cur_item_iter != mItems.end())
{
- if( (*next_item_iter)->getVisible())
+ for (next_item_iter = ++cur_item_iter; next_item_iter != mItems.end(); next_item_iter++)
{
- break;
+ if( (*next_item_iter)->getVisible())
+ {
+ break;
+ }
+ }
+
+ if (next_item_iter != mItems.end() &&
+ (*next_item_iter)->getVisible())
+ {
+ mFirstVisibleItem = *next_item_iter;
}
- }
-
- if ((*next_item_iter)->getVisible())
- {
- mFirstVisibleItem = *next_item_iter;
}
mNeedsArrange = TRUE;
@@ -2228,7 +2258,6 @@ void LLMenuGL::createSpilloverBranch()
LLMenuGL::Params p;
p.name("More");
p.label("More"); // *TODO: Translate
- p.parent_floater(mParentFloaterHandle);
p.bg_color(mBackgroundColor);
p.bg_visible(true);
p.can_tear_off(false);
@@ -2809,7 +2838,7 @@ BOOL LLMenuGL::handleHover( S32 x, S32 y, MASK mask )
((LLMenuItemGL*)viewp)->setHighlight(TRUE);
LLMenuGL::setKeyboardMode(FALSE);
}
- mHasSelection = TRUE;
+ mHasSelection = true;
}
}
}
@@ -2888,7 +2917,7 @@ void LLMenuGL::setVisible(BOOL visible)
}
else
{
- mHasSelection = FALSE;
+ mHasSelection = true;
mFadeTimer.stop();
}
@@ -3316,7 +3345,7 @@ void LLMenuHolderGL::draw()
LLView::draw();
// now draw last selected item as overlay
LLMenuItemGL* selecteditem = (LLMenuItemGL*)sItemLastSelectedHandle.get();
- if (selecteditem && sItemActivationTimer.getStarted() && sItemActivationTimer.getElapsedTimeF32() < ACTIVATE_HIGHLIGHT_TIME)
+ if (selecteditem && selecteditem->getVisible() && sItemActivationTimer.getStarted() && sItemActivationTimer.getElapsedTimeF32() < ACTIVATE_HIGHLIGHT_TIME)
{
// make sure toggle items, for example, show the proper state when fading out
selecteditem->buildDrawLabel();
@@ -3426,7 +3455,7 @@ LLView* const LLMenuHolderGL::getVisibleMenu() const
for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
{
LLView* viewp = *child_it;
- if (viewp->getVisible() && dynamic_cast<LLMenuBarGL*>(viewp) == NULL)
+ if (viewp->getVisible() && dynamic_cast<LLMenuGL*>(viewp) != NULL)
{
return viewp;
}
@@ -3449,8 +3478,7 @@ BOOL LLMenuHolderGL::hideMenus()
for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
{
LLView* viewp = *child_it;
- // clicks off of menu do not hide menu bar
- if (dynamic_cast<LLMenuBarGL*>(viewp) == NULL && viewp->getVisible())
+ if (dynamic_cast<LLMenuGL*>(viewp) != NULL && viewp->getVisible())
{
viewp->setVisible(FALSE);
}
@@ -3936,7 +3964,6 @@ BOOL LLContextMenu::appendContextSubMenu(LLContextMenu *menu)
item = LLUICtrlFactory::create<LLContextMenuBranch>(p);
LLMenuGL::sMenuContainer->addChild(item->getBranch());
- item->setFont( LLFontGL::getFontSansSerif() );
return append( item );
}
diff --git a/indra/llui/llmenugl.h b/indra/llui/llmenugl.h
index 61e06f9e5f..6f0f83d4b9 100644
--- a/indra/llui/llmenugl.h
+++ b/indra/llui/llmenugl.h
@@ -295,7 +295,7 @@ private:
// class, by allowing another method to be specified which determines
// if the menu item should consider itself checked as true or not. Be
// careful that the provided callback is fast - it needs to be VERY
-// FUCKING EFFICIENT, because it may need to be checked a lot.
+// EFFICIENT because it may need to be checked a lot.
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class LLMenuItemCheckGL
@@ -355,7 +355,6 @@ class LLMenuGL
public:
struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
{
- Optional<LLHandle<LLFloater> > parent_floater;
Optional<KEY> jump_key;
Optional<bool> horizontal_layout,
can_tear_off,
@@ -430,7 +429,7 @@ public:
void setBackgroundColor( const LLUIColor& color ) { mBackgroundColor = color; }
const LLUIColor& getBackgroundColor() const { return mBackgroundColor; }
void setBackgroundVisible( BOOL b ) { mBgVisible = b; }
- void setCanTearOff(BOOL tear_off, LLHandle<LLFloater> parent_floater_handle = LLHandle<LLFloater>());
+ void setCanTearOff(BOOL tear_off);
// add a separator to this menu
virtual BOOL addSeparator();
@@ -546,14 +545,13 @@ private:
LLHandle<LLView> mParentMenuItem;
LLUIString mLabel;
BOOL mDropShadowed; // Whether to drop shadow
- BOOL mHasSelection;
+ bool mHasSelection;
LLFrameTimer mFadeTimer;
LLTimer mScrollItemsTimer;
BOOL mTornOff;
class LLMenuItemTearOffGL* mTearOffItem;
class LLMenuItemBranchGL* mSpilloverBranch;
LLMenuGL* mSpilloverMenu;
- LLHandle<LLFloater> mParentFloaterHandle;
KEY mJumpKey;
BOOL mCreateJumpKeys;
S32 mShortcutPad;
@@ -814,7 +812,6 @@ class LLMenuItemTearOffGL : public LLMenuItemGL
public:
struct Params : public LLInitParam::Block<Params, LLMenuItemGL::Params>
{
- Optional<LLHandle<LLFloater> > parent_floater_handle;
Params()
{
name = "tear off";
@@ -823,13 +820,12 @@ public:
};
LLMenuItemTearOffGL( const Params& );
-
+
virtual void onCommit(void);
virtual void draw(void);
virtual U32 getNominalHeight() const;
-private:
- LLHandle<LLFloater> mParentHandle;
+ LLFloater* getParentFloater();
};
diff --git a/indra/llui/llmodaldialog.cpp b/indra/llui/llmodaldialog.cpp
index 387af05935..6cff68c20b 100644
--- a/indra/llui/llmodaldialog.cpp
+++ b/indra/llui/llmodaldialog.cpp
@@ -111,7 +111,7 @@ void LLModalDialog::onOpen(const LLSD& key)
// This is a modal dialog. It sucks up all mouse and keyboard operations.
gFocusMgr.setMouseCapture( this );
- gFocusMgr.setTopCtrl( this );
+ LLUI::addPopup(this);
setFocus(TRUE);
sModalStack.push_front( this );
@@ -153,7 +153,7 @@ void LLModalDialog::setVisible( BOOL visible )
gFocusMgr.setMouseCapture( this );
// The dialog view is a root view
- gFocusMgr.setTopCtrl( this );
+ LLUI::addPopup(this);
setFocus( TRUE );
}
else
@@ -291,7 +291,7 @@ void LLModalDialog::onAppFocusGained()
// This is a modal dialog. It sucks up all mouse and keyboard operations.
gFocusMgr.setMouseCapture( instance );
instance->setFocus(TRUE);
- gFocusMgr.setTopCtrl( instance );
+ LLUI::addPopup(instance);
instance->centerOnScreen();
}
diff --git a/indra/llui/llmultifloater.cpp b/indra/llui/llmultifloater.cpp
index 78738c826d..3aea648562 100644
--- a/indra/llui/llmultifloater.cpp
+++ b/indra/llui/llmultifloater.cpp
@@ -92,14 +92,6 @@ void LLMultiFloater::draw()
}
else
{
- for (S32 i = 0; i < mTabContainer->getTabCount(); i++)
- {
- LLFloater* floaterp = (LLFloater*)mTabContainer->getPanelByIndex(i);
- if (floaterp->getShortTitle() != mTabContainer->getPanelTitle(i))
- {
- mTabContainer->setPanelTitle(i, floaterp->getShortTitle());
- }
- }
LLFloater::draw();
}
}
@@ -353,13 +345,20 @@ void LLMultiFloater::setVisible(BOOL visible)
BOOL LLMultiFloater::handleKeyHere(KEY key, MASK mask)
{
- if (key == 'W' && mask == MASK_CONTROL)
+ if (key == 'W' && mask == (MASK_CONTROL|MASK_SHIFT))
{
LLFloater* floater = getActiveFloater();
// is user closeable and is system closeable
if (floater && floater->canClose() && floater->isCloseable())
{
floater->closeFloater();
+
+ // EXT-5695 (Tabbed IM window loses focus if close any tabs by Ctrl+W)
+ // bring back focus on tab container if there are any tab left
+ if(mTabContainer->getTabCount() > 0)
+ {
+ mTabContainer->setFocus(TRUE);
+ }
}
return TRUE;
}
diff --git a/indra/llui/llmultisliderctrl.cpp b/indra/llui/llmultisliderctrl.cpp
index f4434a0f78..cc26c00a3e 100644
--- a/indra/llui/llmultisliderctrl.cpp
+++ b/indra/llui/llmultisliderctrl.cpp
@@ -138,7 +138,7 @@ LLMultiSliderCtrl::LLMultiSliderCtrl(const LLMultiSliderCtrl::Params& p)
params.font(p.font);
params.max_length_bytes(MAX_STRING_LENGTH);
params.commit_callback.function(LLMultiSliderCtrl::onEditorCommit);
- params.prevalidate_callback(&LLLineEditor::prevalidateFloat);
+ params.prevalidate_callback(&LLTextValidate::validateFloat);
params.follows.flags(FOLLOWS_LEFT | FOLLOWS_BOTTOM);
mEditor = LLUICtrlFactory::create<LLLineEditor> (params);
mEditor->setFocusReceivedCallback( boost::bind(LLMultiSliderCtrl::onEditorGainFocus, _1, this) );
@@ -330,9 +330,14 @@ void LLMultiSliderCtrl::updateText()
// static
void LLMultiSliderCtrl::onEditorCommit( LLUICtrl* ctrl, const LLSD& userdata)
{
- LLMultiSliderCtrl* self = dynamic_cast<LLMultiSliderCtrl*>(ctrl->getParent());
+ llassert(ctrl);
if (!ctrl)
return;
+
+ LLMultiSliderCtrl* self = dynamic_cast<LLMultiSliderCtrl*>(ctrl->getParent());
+ llassert(self);
+ if (!self) // cast failed - wrong type! :O
+ return;
BOOL success = FALSE;
F32 val = self->mCurValue;
diff --git a/indra/llui/llnotifications.cpp b/indra/llui/llnotifications.cpp
index 86989012ee..7b8f51ae3c 100644
--- a/indra/llui/llnotifications.cpp
+++ b/indra/llui/llnotifications.cpp
@@ -48,122 +48,38 @@
const std::string NOTIFICATION_PERSIST_VERSION = "0.93";
-// local channel for notification history
-class LLNotificationHistoryChannel : public LLNotificationChannel
+// 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(LLNotificationHistoryChannel);
+ LOG_CLASS(LLPersistentNotificationChannel);
public:
- LLNotificationHistoryChannel(const std::string& filename) :
- LLNotificationChannel("History", "Visible", &historyFilter, LLNotificationComparators::orderByUUID()),
- mFileName(filename)
+ LLPersistentNotificationChannel() :
+ LLNotificationChannel("Persistent", "Visible", &notificationFilter, LLNotificationComparators::orderByUUID())
{
- connectChanged(boost::bind(&LLNotificationHistoryChannel::historyHandler, this, _1));
- loadPersistentNotifications();
}
private:
- bool historyHandler(const LLSD& payload)
- {
- // we ignore "load" messages, but rewrite the persistence file on any other
- std::string sigtype = payload["sigtype"];
- if (sigtype != "load")
- {
- savePersistentNotifications();
- }
- return false;
- }
- // The history channel gets all notifications except those that have been cancelled
- static bool historyFilter(LLNotificationPtr pNotification)
+ // The channel gets all persistent notifications except those that have been canceled
+ static bool notificationFilter(LLNotificationPtr pNotification)
{
- return !pNotification->isCancelled();
- }
-
- void savePersistentNotifications()
- {
- /* NOTE: As of 2009-11-09 the reload of notifications on startup does not
- work, and has not worked for months. Skip saving notifications until the
- read can be fixed, because this hits the disk once per notification and
- causes log spam. James
-
- llinfos << "Saving open notifications to " << mFileName << llendl;
+ bool handle_notification = false;
- llofstream notify_file(mFileName.c_str());
- if (!notify_file.is_open())
- {
- llwarns << "Failed to open " << mFileName << llendl;
- return;
- }
-
- LLSD output;
- output["version"] = NOTIFICATION_PERSIST_VERSION;
- LLSD& data = output["data"];
-
- for (LLNotificationSet::iterator it = mItems.begin(); it != mItems.end(); ++it)
- {
- if (!LLNotifications::instance().templateExists((*it)->getName())) continue;
+ handle_notification = pNotification->isPersistent()
+ && !pNotification->isCancelled();
- // only store notifications flagged as persisting
- LLNotificationTemplatePtr templatep = LLNotifications::instance().getTemplate((*it)->getName());
- if (!templatep->mPersist) continue;
-
- data.append((*it)->asLLSD());
- }
-
- LLPointer<LLSDFormatter> formatter = new LLSDXMLFormatter();
- formatter->format(output, notify_file, LLSDFormatter::OPTIONS_PRETTY);
- */
+ return handle_notification;
}
- void loadPersistentNotifications()
- {
- llinfos << "Loading open notifications from " << mFileName << llendl;
-
- llifstream notify_file(mFileName.c_str());
- if (!notify_file.is_open())
- {
- llwarns << "Failed to open " << mFileName << llendl;
- return;
- }
-
- LLSD input;
- LLPointer<LLSDParser> parser = new LLSDXMLParser();
- if (parser->parse(notify_file, input, LLSDSerialize::SIZE_UNLIMITED) < 0)
- {
- llwarns << "Failed to parse open notifications" << llendl;
- return;
- }
-
- if (input.isUndefined()) return;
- std::string version = input["version"];
- if (version != NOTIFICATION_PERSIST_VERSION)
- {
- llwarns << "Bad open notifications version: " << version << llendl;
- return;
- }
- LLSD& data = input["data"];
- if (data.isUndefined()) return;
-
- LLNotifications& instance = LLNotifications::instance();
- for (LLSD::array_const_iterator notification_it = data.beginArray();
- notification_it != data.endArray();
- ++notification_it)
- {
- instance.add(LLNotificationPtr(new LLNotification(*notification_it)));
- }
- }
-
- //virtual
void onDelete(LLNotificationPtr pNotification)
{
- // we want to keep deleted notifications in our log
+ // we want to keep deleted notifications in our log, otherwise some
+ // notifications will be lost on exit.
mItems.insert(pNotification);
-
- return;
}
-
-private:
- std::string mFileName;
};
bool filterIgnoredNotifications(LLNotificationPtr notification)
@@ -283,6 +199,7 @@ LLNotificationForm::LLNotificationForm(const std::string& name, const LLXMLNodeP
}
LLNotificationForm::LLNotificationForm(const LLSD& sd)
+ : mIgnore(IGNORE_NO)
{
if (sd.isArray())
{
@@ -384,7 +301,8 @@ LLNotificationTemplate::LLNotificationTemplate() :
mExpireSeconds(0),
mExpireOption(-1),
mURLOption(-1),
- mURLOpenExternally(-1),
+ mURLOpenExternally(-1),
+ mPersist(false),
mUnique(false),
mPriority(NOTIFICATION_PRIORITY_NORMAL)
{
@@ -400,7 +318,9 @@ LLNotification::LLNotification(const LLNotification::Params& p) :
mRespondedTo(false),
mPriority(p.priority),
mCancelled(false),
- mIgnored(false)
+ mIgnored(false),
+ mResponderObj(NULL),
+ mIsReusable(false)
{
if (p.functor.name.isChosen())
{
@@ -413,6 +333,15 @@ LLNotification::LLNotification(const LLNotification::Params& p) :
mTemporaryResponder = true;
}
+ else if(p.functor.responder.isChosen())
+ {
+ mResponder = p.functor.responder;
+ }
+
+ if(p.responder.isProvided())
+ {
+ mResponderObj = p.responder;
+ }
mId.generate();
init(p.name, p.form_elements);
@@ -423,7 +352,9 @@ LLNotification::LLNotification(const LLSD& sd) :
mTemporaryResponder(false),
mRespondedTo(false),
mCancelled(false),
- mIgnored(false)
+ mIgnored(false),
+ mResponderObj(NULL),
+ mIsReusable(false)
{
mId.generate();
mSubstitutions = sd["substitutions"];
@@ -450,6 +381,13 @@ LLSD LLNotification::asLLSD()
output["expiry"] = mExpiresAt;
output["priority"] = (S32)mPriority;
output["responseFunctor"] = mResponseFunctorName;
+ output["reusable"] = mIsReusable;
+
+ if(mResponder)
+ {
+ output["responder"] = mResponder->asLLSD();
+ }
+
return output;
}
@@ -477,7 +415,9 @@ void LLNotification::updateFrom(LLNotificationPtr other)
mForm = other->mForm;
mResponseFunctorName = other->mResponseFunctorName;
mRespondedTo = other->mRespondedTo;
+ mResponse = other->mResponse;
mTemporaryResponder = other->mTemporaryResponder;
+ mIsReusable = other->isReusable();
update();
}
@@ -554,14 +494,24 @@ std::string LLNotification::getSelectedOptionName(const LLSD& response)
void LLNotification::respond(const LLSD& response)
{
+ // *TODO may remove mRespondedTo and use mResponce.isDefined() in isRespondedTo()
mRespondedTo = true;
- // look up the functor
- LLNotificationFunctorRegistry::ResponseFunctor functor =
- LLNotificationFunctorRegistry::instance().getFunctor(mResponseFunctorName);
- // and then call it
- functor(asLLSD(), response);
-
- if (mTemporaryResponder)
+ mResponse = response;
+
+ if(mResponder)
+ {
+ mResponder->handleRespond(asLLSD(), response);
+ }
+ else
+ {
+ // look up the functor
+ LLNotificationFunctorRegistry::ResponseFunctor functor =
+ LLNotificationFunctorRegistry::instance().getFunctor(mResponseFunctorName);
+ // and then call it
+ functor(asLLSD(), response);
+ }
+
+ if (mTemporaryResponder && !isReusable())
{
LLNotificationFunctorRegistry::instance().unregisterFunctor(mResponseFunctorName);
mResponseFunctorName = "";
@@ -595,6 +545,21 @@ void LLNotification::setResponseFunctor(std::string const &responseFunctorName)
mTemporaryResponder = false;
}
+void LLNotification::setResponseFunctor(const LLNotificationFunctorRegistry::ResponseFunctor& cb)
+{
+ if(mTemporaryResponder)
+ {
+ LLNotificationFunctorRegistry::instance().unregisterFunctor(mResponseFunctorName);
+ }
+
+ LLNotificationFunctorRegistry::instance().registerFunctor(mResponseFunctorName, cb);
+}
+
+void LLNotification::setResponseFunctor(const LLNotificationResponderPtr& responder)
+{
+ mResponder = responder;
+}
+
bool LLNotification::payloadContainsAll(const std::vector<std::string>& required_fields) const
{
for(std::vector<std::string>::const_iterator required_fields_it = required_fields.begin();
@@ -854,8 +819,12 @@ bool LLNotificationChannelBase::updateItem(const LLSD& payload, LLNotificationPt
if (wasFound)
{
abortProcessing = mChanged(payload);
- mItems.erase(pNotification);
- onDelete(pNotification);
+ // do not delete the notification to make LLChatHistory::appendMessage add notification panel to IM window
+ if( ! pNotification->isReusable() )
+ {
+ mItems.erase(pNotification);
+ onDelete(pNotification);
+ }
}
}
return abortProcessing;
@@ -1058,6 +1027,7 @@ LLNotificationChannelPtr LLNotifications::getChannel(const std::string& channelN
if(p == mChannels.end())
{
llerrs << "Did not find channel named " << channelName << llendl;
+ return LLNotificationChannelPtr();
}
return p->second;
}
@@ -1085,12 +1055,9 @@ void LLNotifications::createDefaultChannels()
LLNotificationChannel::buildChannel("Visible", "Ignore",
&LLNotificationFilters::includeEverything);
- // create special history channel
- //std::string notifications_log_file = gDirUtilp->getExpandedFilename ( LL_PATH_PER_SL_ACCOUNT, "open_notifications.xml" );
- // use ^^^ when done debugging notifications serialization
- std::string notifications_log_file = gDirUtilp->getExpandedFilename ( LL_PATH_USER_SETTINGS, "open_notifications.xml" );
+ // create special persistent notification channel
// this isn't a leak, don't worry about the empty "new"
- new LLNotificationHistoryChannel(notifications_log_file);
+ new LLPersistentNotificationChannel();
// connect action methods to these channels
LLNotifications::instance().getChannel("Expiration")->
@@ -1521,3 +1488,11 @@ std::ostream& operator<<(std::ostream& s, const LLNotification& notification)
return s;
}
+void LLPostponedNotification::onCachedNameReceived(const LLUUID& id, const std::string& first,
+ const std::string& last, bool is_group)
+{
+ gCacheName->getFullName(id, mName);
+ modifyNotificationParams();
+ LLNotifications::instance().add(mParams);
+ cleanup();
+}
diff --git a/indra/llui/llnotifications.h b/indra/llui/llnotifications.h
index aeb4cebf1b..c942a32512 100644
--- a/indra/llui/llnotifications.h
+++ b/indra/llui/llnotifications.h
@@ -104,6 +104,7 @@
#include "llinitparam.h"
#include "llnotificationslistener.h"
#include "llnotificationptr.h"
+#include "llcachename.h"
typedef enum e_notification_priority
@@ -115,8 +116,23 @@ typedef enum e_notification_priority
NOTIFICATION_PRIORITY_CRITICAL
} ENotificationPriority;
+class LLNotificationResponderInterface
+{
+public:
+ LLNotificationResponderInterface(){};
+ virtual ~LLNotificationResponderInterface(){};
+
+ virtual void handleRespond(const LLSD& notification, const LLSD& response) = 0;
+
+ virtual LLSD asLLSD() = 0;
+
+ virtual void fromLLSD(const LLSD& params) = 0;
+};
+
typedef boost::function<void (const LLSD&, const LLSD&)> LLNotificationResponder;
+typedef boost::shared_ptr<LLNotificationResponderInterface> LLNotificationResponderPtr;
+
typedef LLFunctorRegistry<LLNotificationResponder> LLNotificationFunctorRegistry;
typedef LLFunctorRegistration<LLNotificationResponder> LLNotificationFunctorRegistration;
@@ -296,15 +312,18 @@ public:
Optional<LLSD> form_elements;
Optional<LLDate> time_stamp;
Optional<LLNotificationContext*> context;
+ Optional<void*> responder;
struct Functor : public LLInitParam::Choice<Functor>
{
Alternative<std::string> name;
Alternative<LLNotificationFunctorRegistry::ResponseFunctor> function;
+ Alternative<LLNotificationResponderPtr> responder;
Functor()
: name("functor_name"),
- function("functor")
+ function("functor"),
+ responder("responder")
{}
};
Optional<Functor> functor;
@@ -317,6 +336,7 @@ public:
form_elements("form_elements")
{
time_stamp = LLDate::now();
+ responder = NULL;
}
Params(const std::string& _name)
@@ -329,6 +349,7 @@ public:
functor.name = _name;
name = _name;
time_stamp = LLDate::now();
+ responder = NULL;
}
};
@@ -341,13 +362,17 @@ private:
LLDate mExpiresAt;
bool mCancelled;
bool mRespondedTo; // once the notification has been responded to, this becomes true
+ LLSD mResponse;
bool mIgnored;
ENotificationPriority mPriority;
LLNotificationFormPtr mForm;
-
+ void* mResponderObj; // TODO - refactor/remove this field
+ bool mIsReusable;
+ LLNotificationResponderPtr mResponder;
+
// a reference to the template
LLNotificationTemplatePtr mTemplatep;
-
+
/*
We want to be able to store and reload notifications so that they can survive
a shutdown/restart of the client. So we can't simply pass in callbacks;
@@ -371,7 +396,7 @@ private:
// 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) {}
+ LLNotification(LLUUID uuid) : mId(uuid), mCancelled(false), mRespondedTo(false), mIgnored(false), mPriority(NOTIFICATION_PRIORITY_UNSPECIFIED), mTemporaryResponder(false) {}
void cancel();
@@ -384,6 +409,10 @@ public:
void setResponseFunctor(std::string const &responseFunctorName);
+ void setResponseFunctor(const LLNotificationFunctorRegistry::ResponseFunctor& cb);
+
+ void setResponseFunctor(const LLNotificationResponderPtr& responder);
+
typedef enum e_response_template_type
{
WITHOUT_DEFAULT_BUTTON,
@@ -423,6 +452,10 @@ public:
void respond(const LLSD& sd);
+ void* getResponder() { return mResponderObj; }
+
+ void setResponder(void* responder) { mResponderObj = responder; }
+
void setIgnored(bool ignore);
bool isCancelled() const
@@ -435,6 +468,8 @@ public:
return mRespondedTo;
}
+ const LLSD& getResponse() { return mResponse; }
+
bool isIgnored() const
{
return mIgnored;
@@ -444,7 +479,12 @@ public:
{
return mTemplatep->mName;
}
-
+
+ bool isPersistent() const
+ {
+ return mTemplatep->mPersist;
+ }
+
const LLUUID& id() const
{
return mId;
@@ -504,6 +544,10 @@ 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)
@@ -621,7 +665,7 @@ namespace LLNotificationComparators
struct orderBy
{
typedef boost::function<T (LLNotificationPtr)> field_t;
- orderBy(field_t field, EDirection = ORDER_INCREASING) : mField(field) {}
+ orderBy(field_t field, EDirection direction = ORDER_INCREASING) : mField(field), mDirection(direction) {}
bool operator()(LLNotificationPtr lhs, LLNotificationPtr rhs)
{
if (mDirection == ORDER_DECREASING)
@@ -931,6 +975,62 @@ private:
boost::scoped_ptr<LLNotificationsListener> mListener;
};
+/**
+ * Abstract class for postponed notifications.
+ * Provides possibility to add notification after specified by id avatar or group will be
+ * received from cache name. The object of this type automatically well be deleted
+ * by cleanup method after respond will be received from cache name.
+ *
+ * To add custom postponed notification to the notification system client should:
+ * 1 create class derived from LLPostponedNotification;
+ * 2 call LLPostponedNotification::add method;
+ */
+class LLPostponedNotification
+{
+public:
+ /**
+ * Performs hooking cache name callback which will add notification to notifications system.
+ * Type of added notification should be specified by template parameter T
+ * and non-private derived from LLPostponedNotification class,
+ * otherwise compilation error will occur.
+ */
+ template<class T>
+ static void add(const LLNotification::Params& params,
+ const LLUUID& id, bool is_group)
+ {
+ // upcast T to the base type to restrict T derivation from LLPostponedNotification
+ LLPostponedNotification* thiz = new T();
+
+ thiz->mParams = params;
+
+ gCacheName->get(id, is_group, boost::bind(
+ &LLPostponedNotification::onCachedNameReceived, thiz, _1, _2,
+ _3, _4));
+ }
+
+private:
+ void onCachedNameReceived(const LLUUID& id, const std::string& first,
+ const std::string& last, bool is_group);
+
+ void cleanup()
+ {
+ delete this;
+ }
+
+protected:
+ LLPostponedNotification() {}
+ virtual ~LLPostponedNotification() {}
+
+ /**
+ * Abstract method provides possibility to modify notification parameters and
+ * will be called after cache name retrieve information about avatar or group
+ * and before notification will be added to the notification system.
+ */
+ virtual void modifyNotificationParams() = 0;
+
+ LLNotification::Params mParams;
+ std::string mName;
+};
#endif//LL_LLNOTIFICATIONS_H
diff --git a/indra/llui/llnotificationsutil.cpp b/indra/llui/llnotificationsutil.cpp
index f343d27cb4..54bdb4bd66 100644
--- a/indra/llui/llnotificationsutil.cpp
+++ b/indra/llui/llnotificationsutil.cpp
@@ -94,3 +94,8 @@ void LLNotificationsUtil::cancel(LLNotificationPtr pNotif)
{
LLNotifications::instance().cancel(pNotif);
}
+
+LLNotificationPtr LLNotificationsUtil::find(LLUUID uuid)
+{
+ return LLNotifications::instance().find(uuid);
+}
diff --git a/indra/llui/llnotificationsutil.h b/indra/llui/llnotificationsutil.h
index d552fa915b..338204924a 100644
--- a/indra/llui/llnotificationsutil.h
+++ b/indra/llui/llnotificationsutil.h
@@ -65,6 +65,8 @@ namespace LLNotificationsUtil
S32 getSelectedOption(const LLSD& notification, const LLSD& response);
void cancel(LLNotificationPtr pNotif);
+
+ LLNotificationPtr find(LLUUID uuid);
}
#endif
diff --git a/indra/llui/llpanel.cpp b/indra/llui/llpanel.cpp
index 7f23fe2671..0cd052eefa 100644
--- a/indra/llui/llpanel.cpp
+++ b/indra/llui/llpanel.cpp
@@ -80,6 +80,8 @@ LLPanel::Params::Params()
background_opaque("background_opaque", false),
bg_opaque_color("bg_opaque_color"),
bg_alpha_color("bg_alpha_color"),
+ bg_opaque_image_overlay("bg_opaque_image_overlay"),
+ bg_alpha_image_overlay("bg_alpha_image_overlay"),
bg_opaque_image("bg_opaque_image"),
bg_alpha_image("bg_alpha_image"),
min_width("min_width", 100),
@@ -103,6 +105,8 @@ LLPanel::LLPanel(const LLPanel::Params& p)
mBgOpaque(p.background_opaque),
mBgOpaqueColor(p.bg_opaque_color()),
mBgAlphaColor(p.bg_alpha_color()),
+ mBgOpaqueImageOverlay(p.bg_opaque_image_overlay),
+ mBgAlphaImageOverlay(p.bg_alpha_image_overlay),
mBgOpaqueImage(p.bg_opaque_image()),
mBgAlphaImage(p.bg_alpha_image()),
mDefaultBtn(NULL),
@@ -199,7 +203,7 @@ void LLPanel::draw()
// opaque, in-front look
if (mBgOpaqueImage.notNull())
{
- mBgOpaqueImage->draw( local_rect, UI_VERTEX_COLOR % alpha );
+ mBgOpaqueImage->draw( local_rect, mBgOpaqueImageOverlay % alpha );
}
else
{
@@ -212,7 +216,7 @@ void LLPanel::draw()
// transparent, in-back look
if (mBgAlphaImage.notNull())
{
- mBgAlphaImage->draw( local_rect, UI_VERTEX_COLOR % alpha );
+ mBgAlphaImage->draw( local_rect, mBgAlphaImageOverlay % alpha );
}
else
{
@@ -397,6 +401,12 @@ LLView* LLPanel::fromXML(LLXMLNodePtr node, LLView* parent, LLXMLNodePtr output_
if (!panelp)
{
panelp = LLUICtrlFactory::getInstance()->createFactoryPanel(name);
+ llassert(panelp);
+
+ if (!panelp)
+ {
+ return NULL; // :(
+ }
}
}
@@ -414,7 +424,7 @@ LLView* LLPanel::fromXML(LLXMLNodePtr node, LLView* parent, LLXMLNodePtr output_
panelp->mCommitCallbackRegistrar.popScope();
panelp->mEnableCallbackRegistrar.popScope();
- if (panelp && !panelp->getFactoryMap().empty())
+ if (!panelp->getFactoryMap().empty())
{
LLUICtrlFactory::instance().popFactoryFunctions();
}
@@ -475,6 +485,8 @@ void LLPanel::initFromParams(const LLPanel::Params& p)
setTransparentColor(p.bg_alpha_color().get());
mBgOpaqueImage = p.bg_opaque_image();
mBgAlphaImage = p.bg_alpha_image();
+ mBgOpaqueImageOverlay = p.bg_opaque_image_overlay;
+ mBgAlphaImageOverlay = p.bg_alpha_image_overlay;
}
static LLFastTimer::DeclareTimer FTM_PANEL_SETUP("Panel Setup");
@@ -936,7 +948,7 @@ LLPanel *LLPanel::childGetVisiblePanelWithHelp()
return ::childGetVisiblePanelWithHelp(this);
}
-void LLPanel::childSetPrevalidate(const std::string& id, BOOL (*func)(const LLWString &) )
+void LLPanel::childSetPrevalidate(const std::string& id, bool (*func)(const LLWString &) )
{
LLLineEditor* child = findChild<LLLineEditor>(id);
if (child)
diff --git a/indra/llui/llpanel.h b/indra/llui/llpanel.h
index 6de83fe3a7..03e3dc0c0e 100644
--- a/indra/llui/llpanel.h
+++ b/indra/llui/llpanel.h
@@ -77,7 +77,9 @@ public:
background_opaque;
Optional<LLUIColor> bg_opaque_color,
- bg_alpha_color;
+ bg_alpha_color,
+ bg_opaque_image_overlay,
+ bg_alpha_image_overlay;
// opaque image is for "panel in foreground" look
Optional<LLUIImage*> bg_opaque_image,
bg_alpha_image;
@@ -137,6 +139,8 @@ public:
const LLColor4& getTransparentColor() const { return mBgAlphaColor; }
LLPointer<LLUIImage> getBackgroundImage() const { return mBgOpaqueImage; }
LLPointer<LLUIImage> getTransparentImage() const { return mBgAlphaImage; }
+ LLColor4 getBackgroundImageOverlay() { return mBgOpaqueImageOverlay; }
+ LLColor4 getTransparentImageOverlay() { return mBgAlphaImageOverlay; }
void setBackgroundVisible( BOOL b ) { mBgVisible = b; }
BOOL isBackgroundVisible() const { return mBgVisible; }
void setBackgroundOpaque(BOOL b) { mBgOpaque = b; }
@@ -226,7 +230,7 @@ public:
std::string childGetText(const std::string& id) const { return childGetValue(id).asString(); }
// LLLineEditor
- void childSetPrevalidate(const std::string& id, BOOL (*func)(const LLWString &) );
+ void childSetPrevalidate(const std::string& id, bool (*func)(const LLWString &) );
// LLButton
void childSetAction(const std::string& id, boost::function<void(void*)> function, void* value = NULL);
@@ -262,6 +266,8 @@ private:
BOOL mBgOpaque; // use opaque color or image
LLUIColor mBgOpaqueColor;
LLUIColor mBgAlphaColor;
+ LLUIColor mBgOpaqueImageOverlay;
+ LLUIColor mBgAlphaImageOverlay;
LLPointer<LLUIImage> mBgOpaqueImage; // "panel in front" look
LLPointer<LLUIImage> mBgAlphaImage; // "panel in back" look
LLViewBorder* mBorder;
diff --git a/indra/llui/llradiogroup.cpp b/indra/llui/llradiogroup.cpp
index 4087b484aa..e27792dc1d 100644
--- a/indra/llui/llradiogroup.cpp
+++ b/indra/llui/llradiogroup.cpp
@@ -106,7 +106,6 @@ LLRadioGroup::LLRadioGroup(const LLRadioGroup::Params& p)
void LLRadioGroup::initFromParams(const Params& p)
{
- LLUICtrl::initFromParams(p);
for (LLInitParam::ParamIterator<ItemParams>::const_iterator it = p.items().begin();
it != p.items().end();
++it)
@@ -124,6 +123,9 @@ void LLRadioGroup::initFromParams(const Params& p)
LLRadioCtrl* item = LLUICtrlFactory::create<LLRadioCtrl>(item_params, this);
mRadioButtons.push_back(item);
}
+
+ // call this *after* setting up mRadioButtons so we can handle setValue() calls
+ LLUICtrl::initFromParams(p);
}
@@ -138,10 +140,6 @@ BOOL LLRadioGroup::postBuild()
{
mRadioButtons[0]->setTabStop(true);
}
- if (mControlVariable)
- {
- setSelectedIndex(mControlVariable->getValue().asInteger());
- }
return TRUE;
}
diff --git a/indra/llui/llresizehandle.cpp b/indra/llui/llresizehandle.cpp
index 3df09d124a..00214d451c 100644
--- a/indra/llui/llresizehandle.cpp
+++ b/indra/llui/llresizehandle.cpp
@@ -124,7 +124,7 @@ BOOL LLResizeHandle::handleHover(S32 x, S32 y, MASK mask)
{
// Make sure the mouse in still over the application. We don't want to make the parent
// so big that we can't see the resize handle any more.
-
+
S32 screen_x;
S32 screen_y;
localPointToScreen(x, y, &screen_x, &screen_y);
@@ -136,9 +136,10 @@ BOOL LLResizeHandle::handleHover(S32 x, S32 y, MASK mask)
if( resizing_view )
{
// undock floater when user resize it
- if (((LLFloater*)getParent())->isDocked())
+ LLFloater* floater_parent = dynamic_cast<LLFloater*>(getParent());
+ if (floater_parent && floater_parent->isDocked())
{
- ((LLFloater*)getParent())->setDocked(false, false);
+ floater_parent->setDocked(false, false);
}
// Resize the parent
@@ -146,61 +147,68 @@ BOOL LLResizeHandle::handleHover(S32 x, S32 y, MASK mask)
LLRect scaled_rect = orig_rect;
S32 delta_x = screen_x - mDragLastScreenX;
S32 delta_y = screen_y - mDragLastScreenY;
-
- if(delta_x == 0 && delta_y == 0)
- return FALSE;
-
LLCoordGL mouse_dir;
// use hysteresis on mouse motion to preserve user intent when mouse stops moving
mouse_dir.mX = (screen_x == mLastMouseScreenX) ? mLastMouseDir.mX : screen_x - mLastMouseScreenX;
mouse_dir.mY = (screen_y == mLastMouseScreenY) ? mLastMouseDir.mY : screen_y - mLastMouseScreenY;
-
mLastMouseScreenX = screen_x;
mLastMouseScreenY = screen_y;
mLastMouseDir = mouse_dir;
- S32 new_width = orig_rect.getWidth();
- S32 new_height = orig_rect.getHeight();
+ S32 x_multiple = 1;
+ S32 y_multiple = 1;
+ switch( mCorner )
+ {
+ case LEFT_TOP:
+ x_multiple = -1;
+ y_multiple = 1;
+ break;
+ case LEFT_BOTTOM:
+ x_multiple = -1;
+ y_multiple = -1;
+ break;
+ case RIGHT_TOP:
+ x_multiple = 1;
+ y_multiple = 1;
+ break;
+ case RIGHT_BOTTOM:
+ x_multiple = 1;
+ y_multiple = -1;
+ break;
+ }
- S32 new_pos_x = orig_rect.mLeft;
- S32 new_pos_y = orig_rect.mTop;
+ S32 new_width = orig_rect.getWidth() + x_multiple * delta_x;
+ if( new_width < mMinWidth )
+ {
+ new_width = mMinWidth;
+ delta_x = x_multiple * (mMinWidth - orig_rect.getWidth());
+ }
+
+ S32 new_height = orig_rect.getHeight() + y_multiple * delta_y;
+ if( new_height < mMinHeight )
+ {
+ new_height = mMinHeight;
+ delta_y = y_multiple * (mMinHeight - orig_rect.getHeight());
+ }
switch( mCorner )
{
- case LEFT_TOP:
- new_width-=delta_x;
- new_height+=delta_y;
- new_pos_x+=delta_x;
- new_pos_y+=delta_y;
+ case LEFT_TOP:
+ scaled_rect.translate(delta_x, 0);
break;
case LEFT_BOTTOM:
- new_width-=delta_x;
- new_height-=delta_y;
- new_pos_x+=delta_x;
+ scaled_rect.translate(delta_x, delta_y);
break;
case RIGHT_TOP:
- new_width+=delta_x;
- new_height+=delta_y;
- new_pos_y+=delta_y;
break;
case RIGHT_BOTTOM:
- new_width+=delta_x;
- new_height-=delta_y;
+ scaled_rect.translate(0, delta_y);
break;
}
- new_width = llmax(new_width,mMinWidth);
- new_height = llmax(new_height,mMinHeight);
-
- LLRect::tCoordType screen_width = resizing_view->getParent()->getSnapRect().getWidth();
- LLRect::tCoordType screen_height = resizing_view->getParent()->getSnapRect().getHeight();
-
- new_width = llmin(new_width, screen_width);
- new_height = llmin(new_height, screen_height);
-
// temporarily set new parent rect
- scaled_rect.setLeftTopAndSize(new_pos_x,new_pos_y,new_width,new_height);
-
+ scaled_rect.mRight = scaled_rect.mLeft + new_width;
+ scaled_rect.mTop = scaled_rect.mBottom + new_height;
resizing_view->setRect(scaled_rect);
LLView* snap_view = NULL;
@@ -251,11 +259,7 @@ BOOL LLResizeHandle::handleHover(S32 x, S32 y, MASK mask)
resizing_view->setRect(orig_rect);
// translate and scale to new shape
- resizing_view->reshape(scaled_rect.getWidth(),scaled_rect.getHeight());
- resizing_view->setRect(scaled_rect);
- //set shape to handle dependent floaters...
- resizing_view->handleReshape(scaled_rect, false);
-
+ resizing_view->setShape(scaled_rect, true);
// update last valid mouse cursor position based on resized view's actual size
LLRect new_rect = resizing_view->getRect();
diff --git a/indra/llui/llscrollingpanellist.cpp b/indra/llui/llscrollingpanellist.cpp
index 4f55c0507c..b7840d1b59 100644
--- a/indra/llui/llscrollingpanellist.cpp
+++ b/indra/llui/llscrollingpanellist.cpp
@@ -47,7 +47,12 @@ void LLScrollingPanelList::clearPanels()
{
deleteAllChildren();
mPanelList.clear();
- reshape( 1, 1, FALSE );
+
+ LLRect rc = getRect();
+ rc.setLeftTopAndSize(rc.mLeft, rc.mTop, 1, 1);
+ setRect(rc);
+
+ notifySizeChanged(rc.getHeight());
}
S32 LLScrollingPanelList::addPanel( LLScrollingPanel* panel )
@@ -67,7 +72,11 @@ S32 LLScrollingPanelList::addPanel( LLScrollingPanel* panel )
max_width = llmax( max_width, childp->getRect().getWidth() );
cur_gap = GAP_BETWEEN_PANELS;
}
- reshape( max_width, total_height, FALSE );
+ LLRect rc = getRect();
+ rc.setLeftTopAndSize(rc.mLeft, rc.mTop, max_width, total_height);
+ setRect(rc);
+
+ notifySizeChanged(rc.getHeight());
// Reposition each of the child views
S32 cur_y = total_height;
@@ -131,7 +140,11 @@ void LLScrollingPanelList::removePanel( U32 panel_index )
max_width = llmax( max_width, childp->getRect().getWidth() );
cur_gap = GAP_BETWEEN_PANELS;
}
- reshape( max_width, total_height, FALSE );
+ LLRect rc = getRect();
+ rc.setLeftTopAndSize(rc.mLeft, rc.mTop, max_width, total_height);
+ setRect(rc);
+
+ notifySizeChanged(rc.getHeight());
// Reposition each of the child views
S32 cur_y = total_height;
@@ -200,3 +213,12 @@ void LLScrollingPanelList::draw()
LLUICtrl::draw();
}
+void LLScrollingPanelList::notifySizeChanged(S32 height)
+{
+ LLSD info;
+ info["action"] = "size_changes";
+ info["height"] = height;
+ notifyParent(info);
+}
+
+// EOF
diff --git a/indra/llui/llscrollingpanellist.h b/indra/llui/llscrollingpanellist.h
index 3abfbcbbe7..5f1996159b 100644
--- a/indra/llui/llscrollingpanellist.h
+++ b/indra/llui/llscrollingpanellist.h
@@ -61,7 +61,6 @@ public:
Params()
{
name = "scrolling_panel_list";
- follows.flags = FOLLOWS_LEFT | FOLLOWS_BOTTOM;
}
};
LLScrollingPanelList(const Params& p)
@@ -86,6 +85,11 @@ public:
private:
void updatePanelVisiblilty();
+ /**
+ * Notify parent about size change, makes sense when used inside accordion
+ */
+ void notifySizeChanged(S32 height);
+
panel_list_t mPanelList;
};
diff --git a/indra/llui/llscrolllistcell.cpp b/indra/llui/llscrolllistcell.cpp
index 3cc92baa8d..d17be8b94a 100644
--- a/indra/llui/llscrolllistcell.cpp
+++ b/indra/llui/llscrolllistcell.cpp
@@ -185,6 +185,8 @@ LLScrollListText::LLScrollListText(const LLScrollListCell::Params& p)
{
sCount++;
+ mTextWidth = getWidth();
+
// initialize rounded rect image
if (!mRoundedRectImage)
{
@@ -340,7 +342,7 @@ void LLScrollListText::draw(const LLColor4& color, const LLColor4& highlight_col
0,
LLFontGL::NO_SHADOW,
string_chars,
- getWidth(),
+ getTextWidth(),
&right_x,
TRUE);
}
diff --git a/indra/llui/llscrolllistcell.h b/indra/llui/llscrolllistcell.h
index 5fecf5aade..b1c8901fc4 100644
--- a/indra/llui/llscrolllistcell.h
+++ b/indra/llui/llscrolllistcell.h
@@ -151,11 +151,16 @@ public:
/*virtual*/ const std::string & getToolTip() const;
/*virtual*/ BOOL needsToolTip() const;
+ S32 getTextWidth() const { return mTextWidth;}
+ void setTextWidth(S32 value) { mTextWidth = value;}
+ virtual void setWidth(S32 width) { LLScrollListCell::setWidth(width); mTextWidth = width; }
+
void setText(const LLStringExplicit& text);
void setFontStyle(const U8 font_style);
private:
LLUIString mText;
+ S32 mTextWidth;
const LLFontGL* mFont;
LLColor4 mColor;
U8 mUseColor;
diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp
index 4e84013db0..94eade06ad 100644
--- a/indra/llui/llscrolllistctrl.cpp
+++ b/indra/llui/llscrolllistctrl.cpp
@@ -71,8 +71,9 @@ static LLDefaultChildRegistry::Register<LLScrollListCtrl> r("scroll_list");
// local structures & classes.
struct SortScrollListItem
{
- SortScrollListItem(const std::vector<std::pair<S32, BOOL> >& sort_orders)
+ SortScrollListItem(const std::vector<std::pair<S32, BOOL> >& sort_orders,const LLScrollListCtrl::sort_signal_t* sort_signal)
: mSortOrders(sort_orders)
+ , mSortSignal(sort_signal)
{}
bool operator()(const LLScrollListItem* i1, const LLScrollListItem* i2)
@@ -85,12 +86,20 @@ struct SortScrollListItem
S32 col_idx = it->first;
BOOL sort_ascending = it->second;
+ S32 order = sort_ascending ? 1 : -1; // ascending or descending sort for this column?
+
const LLScrollListCell *cell1 = i1->getColumn(col_idx);
const LLScrollListCell *cell2 = i2->getColumn(col_idx);
- S32 order = sort_ascending ? 1 : -1; // ascending or descending sort for this column?
if (cell1 && cell2)
{
- sort_result = order * LLStringUtil::compareDict(cell1->getValue().asString(), cell2->getValue().asString());
+ if(mSortSignal)
+ {
+ sort_result = order * (*mSortSignal)(col_idx,i1, i2);
+ }
+ else
+ {
+ sort_result = order * LLStringUtil::compareDict(cell1->getValue().asString(), cell2->getValue().asString());
+ }
if (sort_result != 0)
{
break; // we have a sort order!
@@ -100,8 +109,10 @@ struct SortScrollListItem
return sort_result < 0;
}
+
typedef std::vector<std::pair<S32, BOOL> > sort_order_t;
+ const LLScrollListCtrl::sort_signal_t* mSortSignal;
const sort_order_t& mSortOrders;
};
@@ -169,6 +180,7 @@ LLScrollListCtrl::LLScrollListCtrl(const LLScrollListCtrl::Params& p)
mOnSortChangedCallback( NULL ),
mHighlightedItem(-1),
mBorder(NULL),
+ mSortCallback(NULL),
mPopupMenu(NULL),
mNumDynamicWidthColumns(0),
mTotalStaticColumnWidth(0),
@@ -270,6 +282,8 @@ LLScrollListCtrl::LLScrollListCtrl(const LLScrollListCtrl::Params& p)
text_p.border_visible(false);
text_p.rect(mItemListRect);
text_p.follows.flags(FOLLOWS_ALL);
+ // word wrap was added accroding to the EXT-6841
+ text_p.wrap(true);
addChild(LLUICtrlFactory::create<LLTextBox>(text_p));
}
@@ -309,12 +323,9 @@ bool LLScrollListCtrl::preProcessChildNode(LLXMLNodePtr child)
LLScrollListCtrl::~LLScrollListCtrl()
{
- std::for_each(mItemList.begin(), mItemList.end(), DeletePointer());
+ delete mSortCallback;
- if( gEditMenuHandler == this )
- {
- gEditMenuHandler = NULL;
- }
+ std::for_each(mItemList.begin(), mItemList.end(), DeletePointer());
}
@@ -498,7 +509,7 @@ void LLScrollListCtrl::fitContents(S32 max_width, S32 max_height)
{
S32 height = llmin( getRequiredRect().getHeight(), max_height );
if(mPageLines)
- height = llmin( mPageLines * mLineHeight + (mDisplayColumnHeaders ? mHeadingHeight : 0), height );
+ height = llmin( mPageLines * mLineHeight + 2*mBorderThickness + (mDisplayColumnHeaders ? mHeadingHeight : 0), height );
S32 width = getRect().getWidth();
@@ -540,7 +551,7 @@ BOOL LLScrollListCtrl::addItem( LLScrollListItem* item, EAddPosition pos, BOOL r
std::stable_sort(
mItemList.begin(),
mItemList.end(),
- SortScrollListItem(single_sort_column));
+ SortScrollListItem(single_sort_column,mSortCallback));
// ADD_SORTED just sorts by first column...
// this might not match user sort criteria, so flag list as being in unsorted state
@@ -943,14 +954,14 @@ void LLScrollListCtrl::mouseOverHighlightNthItem(S32 target_index)
}
}
-S32 LLScrollListCtrl::selectMultiple( std::vector<LLUUID> ids )
+S32 LLScrollListCtrl::selectMultiple( uuid_vec_t ids )
{
item_list::iterator iter;
S32 count = 0;
for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
{
LLScrollListItem* item = *iter;
- std::vector<LLUUID>::iterator iditr;
+ uuid_vec_t::iterator iditr;
for(iditr = ids.begin(); iditr != ids.end(); ++iditr)
{
if (item->getEnabled() && (item->getUUID() == (*iditr)))
@@ -1388,6 +1399,8 @@ void LLScrollListCtrl::drawItems()
LLGLSUIDefault gls_ui;
+ F32 alpha = getDrawContext().mAlpha;
+
{
LLLocalClipRect clip(mItemListRect);
@@ -1463,7 +1476,7 @@ void LLScrollListCtrl::drawItems()
bg_color = mBgReadOnlyColor.get();
}
- item->draw(item_rect, fg_color, bg_color, highlight_color, mColumnPadding);
+ item->draw(item_rect, fg_color % alpha, bg_color% alpha, highlight_color % alpha, mColumnPadding);
cur_y -= mLineHeight;
}
@@ -1534,7 +1547,7 @@ LLRect LLScrollListCtrl::getCellRect(S32 row_index, S32 column_index)
S32 rect_bottom = getRowOffsetFromIndex(row_index);
LLScrollListColumn* columnp = getColumn(column_index);
cell_rect.setOriginAndSize(rect_left, rect_bottom,
- rect_left + columnp->getWidth(), mLineHeight);
+ /*rect_left + */columnp->getWidth(), mLineHeight);
return cell_rect;
}
@@ -2393,7 +2406,7 @@ void LLScrollListCtrl::updateSort() const
std::stable_sort(
mItemList.begin(),
mItemList.end(),
- SortScrollListItem(mSortColumns));
+ SortScrollListItem(mSortColumns,mSortCallback));
mSorted = true;
}
@@ -2409,7 +2422,7 @@ void LLScrollListCtrl::sortOnce(S32 column, BOOL ascending)
std::stable_sort(
mItemList.begin(),
mItemList.end(),
- SortScrollListItem(sort_column));
+ SortScrollListItem(sort_column,mSortCallback));
}
void LLScrollListCtrl::dirtyColumns()
@@ -2760,9 +2773,13 @@ LLScrollListItem* LLScrollListCtrl::addElement(const LLSD& element, EAddPosition
LLScrollListItem* LLScrollListCtrl::addRow(const LLScrollListItem::Params& item_p, EAddPosition pos)
{
- if (!item_p.validateBlock()) return NULL;
-
LLScrollListItem *new_item = new LLScrollListItem(item_p);
+ return addRow(new_item, item_p, pos);
+}
+
+LLScrollListItem* LLScrollListCtrl::addRow(LLScrollListItem *new_item, const LLScrollListItem::Params& item_p, EAddPosition pos)
+{
+ if (!item_p.validateBlock() || !new_item) return NULL;
new_item->setNumColumns(mColumns.size());
// Add any columns we don't already have
diff --git a/indra/llui/llscrolllistctrl.h b/indra/llui/llscrolllistctrl.h
index 907dc90bea..1f0ef585db 100644
--- a/indra/llui/llscrolllistctrl.h
+++ b/indra/llui/llscrolllistctrl.h
@@ -73,6 +73,30 @@ public:
// *TODO: Add callbacks to Params
typedef boost::function<void (void)> callback_t;
+
+ template<typename T> struct maximum
+ {
+ typedef T result_type;
+
+ template<typename InputIterator>
+ T operator()(InputIterator first, InputIterator last) const
+ {
+ // If there are no slots to call, just return the
+ // default-constructed value
+ if(first == last ) return T();
+ T max_value = *first++;
+ while (first != last) {
+ if (max_value < *first)
+ max_value = *first;
+ ++first;
+ }
+
+ return max_value;
+ }
+ };
+
+
+ typedef boost::signals2::signal<S32 (S32,const LLScrollListItem*,const LLScrollListItem*),maximum<S32> > sort_signal_t;
struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
{
@@ -148,6 +172,7 @@ public:
// "columns" => [ "column" => column name, "value" => value, "type" => type, "font" => font, "font-style" => style ], "id" => uuid
// Creates missing columns automatically.
virtual LLScrollListItem* addElement(const LLSD& element, EAddPosition pos = ADD_BOTTOM, void* userdata = NULL);
+ virtual LLScrollListItem* addRow(LLScrollListItem *new_item, const LLScrollListItem::Params& value, EAddPosition pos = ADD_BOTTOM);
virtual LLScrollListItem* addRow(const LLScrollListItem::Params& value, EAddPosition pos = ADD_BOTTOM);
// Simple add element. Takes a single array of:
// [ "value" => value, "font" => font, "font-style" => style ]
@@ -194,7 +219,10 @@ public:
void deselectAllItems(BOOL no_commit_on_change = FALSE); // by default, go ahead and commit on selection change
void clearHighlightedItems();
- void mouseOverHighlightNthItem( S32 index );
+
+ virtual void mouseOverHighlightNthItem( S32 index );
+
+ S32 getHighlightedItemInx() const { return mHighlightedItem; }
void setDoubleClickCallback( callback_t cb ) { mOnDoubleClickCallback = cb; }
void setMaximumSelectCallback( callback_t cb) { mOnMaximumSelectCallback = cb; }
@@ -351,7 +379,7 @@ public:
BOOL getSortAscending() { return mSortColumns.empty() ? TRUE : mSortColumns.back().second; }
BOOL hasSortOrder() const;
- S32 selectMultiple( std::vector<LLUUID> ids );
+ S32 selectMultiple( uuid_vec_t ids );
// conceptually const, but mutates mItemList
void updateSort() const;
// sorts a list without affecting the permanent sort order (so further list insertions can be unsorted, for example)
@@ -361,6 +389,13 @@ public:
void setNeedsSort(bool val = true) { mSorted = !val; }
void dirtyColumns(); // some operation has potentially affected column layout or ordering
+ boost::signals2::connection setSortCallback(sort_signal_t::slot_type cb )
+ {
+ if (!mSortCallback) mSortCallback = new sort_signal_t();
+ return mSortCallback->connect(cb);
+ }
+
+
protected:
// "Full" interface: use this when you're creating a list that has one or more of the following:
// * contains icons
@@ -473,6 +508,8 @@ private:
typedef std::pair<S32, BOOL> sort_column_t;
std::vector<sort_column_t> mSortColumns;
+
+ sort_signal_t* mSortCallback;
}; // end class LLScrollListCtrl
#endif // LL_SCROLLLISTCTRL_H
diff --git a/indra/llui/llscrolllistitem.h b/indra/llui/llscrolllistitem.h
index 15b86cc945..25ce846d90 100644
--- a/indra/llui/llscrolllistitem.h
+++ b/indra/llui/llscrolllistitem.h
@@ -95,7 +95,7 @@ public:
void setUserdata( void* userdata ) { mUserdata = userdata; }
void* getUserdata() const { return mUserdata; }
- LLUUID getUUID() const { return mItemValue.asUUID(); }
+ virtual LLUUID getUUID() const { return mItemValue.asUUID(); }
LLSD getValue() const { return mItemValue; }
void setRect(LLRect rect) { mRectangle = rect; }
diff --git a/indra/llui/llsearcheditor.cpp b/indra/llui/llsearcheditor.cpp
index 6fa99df82e..ec2ad5e5fa 100644
--- a/indra/llui/llsearcheditor.cpp
+++ b/indra/llui/llsearcheditor.cpp
@@ -44,22 +44,34 @@ LLSearchEditor::LLSearchEditor(const LLSearchEditor::Params& p)
S32 srch_btn_top = p.search_button.top_pad + p.search_button.rect.height;
S32 srch_btn_right = p.search_button.rect.width + p.search_button.left_pad;
LLRect srch_btn_rect(p.search_button.left_pad, srch_btn_top, srch_btn_right, p.search_button.top_pad);
+
+ S32 clr_btn_top = p.clear_button.rect.bottom + p.clear_button.rect.height;
+ S32 clr_btn_right = getRect().getWidth() - p.clear_button.pad_right;
+ S32 clr_btn_left = clr_btn_right - p.clear_button.rect.width;
+ LLRect clear_btn_rect(clr_btn_left, clr_btn_top, clr_btn_right, p.clear_button.rect.bottom);
+
S32 text_pad_left = p.text_pad_left;
+ S32 text_pad_right = p.text_pad_right;
if (p.search_button_visible)
text_pad_left += srch_btn_rect.getWidth();
+ if (p.clear_button_visible)
+ text_pad_right = getRect().getWidth() - clr_btn_left + p.clear_button.pad_left;
+
// Set up line editor.
LLLineEditor::Params line_editor_params(p);
line_editor_params.name("filter edit box");
line_editor_params.rect(getLocalRect());
line_editor_params.follows.flags(FOLLOWS_ALL);
line_editor_params.text_pad_left(text_pad_left);
+ line_editor_params.text_pad_right(text_pad_right);
line_editor_params.revert_on_esc(false);
line_editor_params.commit_callback.function(boost::bind(&LLUICtrl::onCommit, this));
line_editor_params.keystroke_callback(boost::bind(&LLSearchEditor::handleKeystroke, this));
mSearchEditor = LLUICtrlFactory::create<LLLineEditor>(line_editor_params);
+ mSearchEditor->setPassDelete(TRUE);
addChild(mSearchEditor);
if (p.search_button_visible)
@@ -79,8 +91,6 @@ LLSearchEditor::LLSearchEditor(const LLSearchEditor::Params& p)
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) ;
@@ -153,7 +163,7 @@ void LLSearchEditor::setFocus( BOOL b )
void LLSearchEditor::onClearButtonClick(const LLSD& data)
{
setText(LLStringUtil::null);
- mSearchEditor->doDelete(); // force keystroke callback
+ mSearchEditor->onCommit(); // force keystroke callback
}
void LLSearchEditor::handleKeystroke()
diff --git a/indra/llui/llsearcheditor.h b/indra/llui/llsearcheditor.h
index 714aca9337..785d0633dc 100644
--- a/indra/llui/llsearcheditor.h
+++ b/indra/llui/llsearcheditor.h
@@ -66,6 +66,8 @@ public:
}
};
+ void setCommitOnFocusLost(BOOL b) { if (mSearchEditor) mSearchEditor->setCommitOnFocusLost(b); }
+
protected:
LLSearchEditor(const Params&);
friend class LLUICtrlFactory;
diff --git a/indra/llui/llsliderctrl.cpp b/indra/llui/llsliderctrl.cpp
index 01c274bb4e..04958075db 100644
--- a/indra/llui/llsliderctrl.cpp
+++ b/indra/llui/llsliderctrl.cpp
@@ -63,7 +63,8 @@ LLSliderCtrl::LLSliderCtrl(const LLSliderCtrl::Params& p)
mCanEditText(p.can_edit_text),
mPrecision(p.decimal_digits),
mTextEnabledColor(p.text_color()),
- mTextDisabledColor(p.text_disabled_color())
+ mTextDisabledColor(p.text_disabled_color()),
+ mLabelWidth(p.label_width)
{
S32 top = getRect().getHeight();
S32 bottom = 0;
@@ -86,6 +87,7 @@ LLSliderCtrl::LLSliderCtrl(const LLSliderCtrl::Params& p)
params.initial_value(p.label());
mLabelBox = LLUICtrlFactory::create<LLTextBox> (params);
addChild(mLabelBox);
+ mLabelFont = params.font();
}
if (p.show_text && !p.text_width.isProvided())
@@ -141,7 +143,7 @@ LLSliderCtrl::LLSliderCtrl(const LLSliderCtrl::Params& p)
line_p.rect.setIfNotProvided(text_rect);
line_p.font.setIfNotProvided(p.font);
line_p.commit_callback.function(&LLSliderCtrl::onEditorCommit);
- line_p.prevalidate_callback(&LLLineEditor::prevalidateFloat);
+ line_p.prevalidate_callback(&LLTextValidate::validateFloat);
mEditor = LLUICtrlFactory::create<LLLineEditor>(line_p);
mEditor->setFocusReceivedCallback( boost::bind(&LLSliderCtrl::onEditorGainFocus, _1, this ));
@@ -186,9 +188,9 @@ BOOL LLSliderCtrl::setLabelArg( const std::string& key, const LLStringExplicit&
if (mLabelBox)
{
res = mLabelBox->setTextArg(key, text);
- if (res && mLabelWidth == 0)
+ if (res && mLabelFont && mLabelWidth == 0)
{
- S32 label_width = mFont->getWidth(mLabelBox->getText());
+ S32 label_width = mLabelFont->getWidth(mLabelBox->getText());
LLRect rect = mLabelBox->getRect();
S32 prev_right = rect.mRight;
rect.mRight = rect.mLeft + label_width;
diff --git a/indra/llui/llsliderctrl.h b/indra/llui/llsliderctrl.h
index c425849782..482c81a0f4 100644
--- a/indra/llui/llsliderctrl.h
+++ b/indra/llui/llsliderctrl.h
@@ -141,6 +141,7 @@ private:
void reportInvalidData();
const LLFontGL* mFont;
+ const LLFontGL* mLabelFont;
BOOL mShowText;
BOOL mCanEditText;
@@ -158,3 +159,4 @@ private:
};
#endif // LL_LLSLIDERCTRL_H
+
diff --git a/indra/llui/llspinctrl.cpp b/indra/llui/llspinctrl.cpp
index 20a1ab7af3..b47f21ed8a 100644
--- a/indra/llui/llspinctrl.cpp
+++ b/indra/llui/llspinctrl.cpp
@@ -127,7 +127,7 @@ LLSpinCtrl::LLSpinCtrl(const LLSpinCtrl::Params& p)
}
params.max_length_bytes(MAX_STRING_LENGTH);
params.commit_callback.function((boost::bind(&LLSpinCtrl::onEditorCommit, this, _2)));
- params.prevalidate_callback(&LLLineEditor::prevalidateFloat);
+ params.prevalidate_callback(&LLTextValidate::validateFloat);
params.follows.flags(FOLLOWS_LEFT | FOLLOWS_BOTTOM);
mEditor = LLUICtrlFactory::create<LLLineEditor> (params);
mEditor->setFocusReceivedCallback( boost::bind(&LLSpinCtrl::onEditorGainFocus, _1, this ));
@@ -270,13 +270,19 @@ void LLSpinCtrl::clear()
mbHasBeenSet = FALSE;
}
-
+void LLSpinCtrl::updateLabelColor()
+{
+ if( mLabelBox )
+ {
+ mLabelBox->setColor( getEnabled() ? mTextEnabledColor.get() : mTextDisabledColor.get() );
+ }
+}
void LLSpinCtrl::updateEditor()
{
LLLocale locale(LLLocale::USER_LOCALE);
- // Don't display very small negative values as -0.000
+ // Don't display very small negative valu es as -0.000
F32 displayed_value = clamp_precision((F32)getValue().asReal(), mPrecision);
// if( S32( displayed_value * pow( 10, mPrecision ) ) == 0 )
@@ -339,10 +345,7 @@ void LLSpinCtrl::setEnabled(BOOL b)
{
LLView::setEnabled( b );
mEditor->setEnabled( b );
- if( mLabelBox )
- {
- mLabelBox->setColor( b ? mTextEnabledColor.get() : mTextDisabledColor.get() );
- }
+ updateLabelColor();
}
@@ -390,6 +393,7 @@ void LLSpinCtrl::setLabel(const LLStringExplicit& label)
{
llwarns << "Attempting to set label on LLSpinCtrl constructed without one " << getName() << llendl;
}
+ updateLabelColor();
}
void LLSpinCtrl::setAllowEdit(BOOL allow_edit)
@@ -453,3 +457,8 @@ BOOL LLSpinCtrl::handleKeyHere(KEY key, MASK mask)
return FALSE;
}
+BOOL LLSpinCtrl::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ // just treat a double click as a second click
+ return handleMouseDown(x, y, mask);
+}
diff --git a/indra/llui/llspinctrl.h b/indra/llui/llspinctrl.h
index 0e610b7741..06201255d2 100644
--- a/indra/llui/llspinctrl.h
+++ b/indra/llui/llspinctrl.h
@@ -81,8 +81,8 @@ public:
virtual void setPrecision(S32 precision);
void setLabel(const LLStringExplicit& label);
- void setLabelColor(const LLColor4& c) { mTextEnabledColor = c; }
- void setDisabledLabelColor(const LLColor4& c) { mTextDisabledColor = c; }
+ void setLabelColor(const LLColor4& c) { mTextEnabledColor = c; updateLabelColor(); }
+ void setDisabledLabelColor(const LLColor4& c) { mTextDisabledColor = c; updateLabelColor();}
void setAllowEdit(BOOL allow_edit);
virtual void onTabInto();
@@ -94,6 +94,7 @@ public:
virtual BOOL handleScrollWheel(S32 x,S32 y,S32 clicks);
virtual BOOL handleKeyHere(KEY key, MASK mask);
+ virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask);
void onEditorCommit(const LLSD& data);
static void onEditorGainFocus(LLFocusableElement* caller, void *userdata);
@@ -103,6 +104,7 @@ public:
void onDownBtn(const LLSD& data);
private:
+ void updateLabelColor();
void updateEditor();
void reportInvalidData();
diff --git a/indra/llui/llstyle.cpp b/indra/llui/llstyle.cpp
index 71511f69a4..b8f93b6a0e 100644
--- a/indra/llui/llstyle.cpp
+++ b/indra/llui/llstyle.cpp
@@ -49,7 +49,10 @@ LLStyle::Params::Params()
LLStyle::LLStyle(const LLStyle::Params& p)
-: mVisible(p.visible),
+: mItalic(FALSE),
+ mBold(FALSE),
+ mUnderline(FALSE),
+ mVisible(p.visible),
mColor(p.color()),
mReadOnlyColor(p.readonly_color()),
mFont(p.font()),
diff --git a/indra/llui/llstyle.h b/indra/llui/llstyle.h
index ee9ca730e9..2067e8e8be 100644
--- a/indra/llui/llstyle.h
+++ b/indra/llui/llstyle.h
@@ -59,11 +59,12 @@ public:
void setColor(const LLColor4 &color) { mColor = color; }
const LLColor4& getReadOnlyColor() const { return mReadOnlyColor; }
+ void setReadOnlyColor(const LLColor4& color) { mReadOnlyColor = color; }
BOOL isVisible() const;
void setVisible(BOOL is_visible);
- LLFontGL::ShadowType getShadowType() { return mDropShadow; }
+ LLFontGL::ShadowType getShadowType() const { return mDropShadow; }
void setFont(const LLFontGL* font);
const LLFontGL* getFont() const;
@@ -116,5 +117,6 @@ private:
};
typedef LLPointer<LLStyle> LLStyleSP;
+typedef LLPointer<const LLStyle> LLStyleConstSP;
#endif // LL_LLSTYLE_H
diff --git a/indra/llui/lltabcontainer.cpp b/indra/llui/lltabcontainer.cpp
index 327dd01612..30fc7babae 100644
--- a/indra/llui/lltabcontainer.cpp
+++ b/indra/llui/lltabcontainer.cpp
@@ -35,7 +35,6 @@
#include "lltabcontainer.h"
#include "llfocusmgr.h"
-#include "llbutton.h"
#include "lllocalcliprect.h"
#include "llrect.h"
#include "llresizehandle.h"
@@ -96,6 +95,95 @@ public:
//----------------------------------------------------------------------------
+//============================================================================
+/*
+ * @file lltabcontainer.cpp
+ * @brief class implements LLButton with LLIconCtrl on it
+ */
+class LLCustomButtonIconCtrl : public LLButton
+{
+public:
+ struct Params
+ : public LLInitParam::Block<Params, LLButton::Params>
+ {
+ // LEFT, RIGHT, TOP, BOTTOM paddings of LLIconCtrl in this class has same value
+ Optional<S32> icon_ctrl_pad;
+
+ Params():
+ icon_ctrl_pad("icon_ctrl_pad", 1)
+ {}
+ };
+
+protected:
+ friend class LLUICtrlFactory;
+ LLCustomButtonIconCtrl(const Params& p):
+ LLButton(p),
+ mIcon(NULL),
+ mIconAlignment(LLFontGL::HCENTER),
+ mIconCtrlPad(p.icon_ctrl_pad)
+ {}
+
+public:
+
+ void updateLayout()
+ {
+ LLRect button_rect = getRect();
+ LLRect icon_rect = mIcon->getRect();
+
+ S32 icon_size = button_rect.getHeight() - 2*mIconCtrlPad;
+
+ switch(mIconAlignment)
+ {
+ case LLFontGL::LEFT:
+ icon_rect.setLeftTopAndSize(button_rect.mLeft + mIconCtrlPad, button_rect.mTop - mIconCtrlPad,
+ icon_size, icon_size);
+ setLeftHPad(icon_size + mIconCtrlPad * 2);
+ break;
+ case LLFontGL::HCENTER:
+ icon_rect.setLeftTopAndSize(button_rect.mRight - (button_rect.getWidth() + mIconCtrlPad - icon_size)/2, button_rect.mTop - mIconCtrlPad,
+ icon_size, icon_size);
+ setRightHPad(icon_size + mIconCtrlPad * 2);
+ break;
+ case LLFontGL::RIGHT:
+ icon_rect.setLeftTopAndSize(button_rect.mRight - mIconCtrlPad - icon_size, button_rect.mTop - mIconCtrlPad,
+ icon_size, icon_size);
+ setRightHPad(icon_size + mIconCtrlPad * 2);
+ break;
+ default:
+ break;
+ }
+ mIcon->setRect(icon_rect);
+ }
+
+ void setIcon(LLIconCtrl* icon, LLFontGL::HAlign alignment = LLFontGL::LEFT)
+ {
+ if(icon)
+ {
+ if(mIcon)
+ {
+ removeChild(mIcon);
+ mIcon->die();
+ }
+ mIcon = icon;
+ mIconAlignment = alignment;
+
+ addChild(mIcon);
+ updateLayout();
+ }
+ }
+
+ LLIconCtrl* getIconCtrl() const
+ {
+ return mIcon;
+ }
+
+private:
+ LLIconCtrl* mIcon;
+ LLFontGL::HAlign mIconAlignment;
+ S32 mIconCtrlPad;
+};
+//============================================================================
+
struct LLPlaceHolderPanel : public LLPanel
{
// create dummy param block to register with "placeholder" nane
@@ -127,7 +215,11 @@ LLTabContainer::Params::Params()
tab_padding_right("tab_padding_right"),
first_tab("first_tab"),
middle_tab("middle_tab"),
- last_tab("last_tab")
+ last_tab("last_tab"),
+ use_custom_icon_ctrl("use_custom_icon_ctrl", false),
+ tab_icon_ctrl_pad("tab_icon_ctrl_pad", 0),
+ use_ellipses("use_ellipses"),
+ font_halign("halign")
{
name(std::string("tab_container"));
mouse_opaque = false;
@@ -162,7 +254,10 @@ LLTabContainer::LLTabContainer(const LLTabContainer::Params& p)
mFont(p.font),
mFirstTabParams(p.first_tab),
mMiddleTabParams(p.middle_tab),
- mLastTabParams(p.last_tab)
+ mLastTabParams(p.last_tab),
+ mCustomIconCtrlUsed(p.use_custom_icon_ctrl),
+ mTabIconCtrlPad(p.tab_icon_ctrl_pad),
+ mUseTabEllipses(p.use_ellipses)
{
static LLUICachedControl<S32> tabcntr_vert_tab_min_width ("UITabCntrVertTabMinWidth", 0);
@@ -402,15 +497,15 @@ void LLTabContainer::draw()
if( mIsVertical && has_scroll_arrows )
{
// Redraw the arrows so that they appears on top.
- gGL.pushMatrix();
- gGL.translatef((F32)mPrevArrowBtn->getRect().mLeft, (F32)mPrevArrowBtn->getRect().mBottom, 0.f);
+ gGL.pushUIMatrix();
+ gGL.translateUI((F32)mPrevArrowBtn->getRect().mLeft, (F32)mPrevArrowBtn->getRect().mBottom, 0.f);
mPrevArrowBtn->draw();
- gGL.popMatrix();
+ gGL.popUIMatrix();
- gGL.pushMatrix();
- gGL.translatef((F32)mNextArrowBtn->getRect().mLeft, (F32)mNextArrowBtn->getRect().mBottom, 0.f);
+ gGL.pushUIMatrix();
+ gGL.translateUI((F32)mNextArrowBtn->getRect().mLeft, (F32)mNextArrowBtn->getRect().mBottom, 0.f);
mNextArrowBtn->draw();
- gGL.popMatrix();
+ gGL.popUIMatrix();
}
}
@@ -801,6 +896,10 @@ void LLTabContainer::update_images(LLTabTuple* tuple, TabParams params, LLTabCon
void LLTabContainer::addTabPanel(const TabPanelParams& panel)
{
LLPanel* child = panel.panel();
+
+ llassert(child);
+ if (!child) return;
+
const std::string& label = panel.label.isProvided()
? panel.label()
: panel.panel()->getLabel();
@@ -856,7 +955,7 @@ void LLTabContainer::addTabPanel(const TabPanelParams& panel)
LLRect tab_panel_rect;
if (!getTabsHidden() && mIsVertical)
{
- tab_panel_rect = LLRect(mMinTabWidth + (LLPANEL_BORDER_WIDTH * 2) + tabcntrv_pad,
+ tab_panel_rect = LLRect(mMinTabWidth + mRightTabBtnOffset + (LLPANEL_BORDER_WIDTH * 2) + tabcntrv_pad,
getRect().getHeight() - LLPANEL_BORDER_WIDTH,
getRect().getWidth() - LLPANEL_BORDER_WIDTH,
LLPANEL_BORDER_WIDTH);
@@ -905,6 +1004,11 @@ void LLTabContainer::addTabPanel(const TabPanelParams& panel)
LLTextBox* textbox = NULL;
LLButton* btn = NULL;
+ LLCustomButtonIconCtrl::Params custom_btn_params;
+ {
+ custom_btn_params.icon_ctrl_pad(mTabIconCtrlPad);
+ }
+ LLButton::Params normal_btn_params;
if (placeholder)
{
@@ -924,7 +1028,9 @@ void LLTabContainer::addTabPanel(const TabPanelParams& panel)
{
if (mIsVertical)
{
- LLButton::Params p;
+ LLButton::Params& p = (mCustomIconCtrlUsed)?
+ custom_btn_params:normal_btn_params;
+
p.name(std::string("vert tab button"));
p.rect(btn_rect);
p.follows.flags(FOLLOWS_TOP | FOLLOWS_LEFT);
@@ -942,11 +1048,22 @@ void LLTabContainer::addTabPanel(const TabPanelParams& panel)
{
p.pad_left(indent);
}
- btn = LLUICtrlFactory::create<LLButton>(p);
+
+
+ if(mCustomIconCtrlUsed)
+ {
+ btn = LLUICtrlFactory::create<LLCustomButtonIconCtrl>(custom_btn_params);
+
+ }
+ else
+ {
+ btn = LLUICtrlFactory::create<LLButton>(p);
+ }
}
else
{
- LLButton::Params p;
+ LLButton::Params& p = (mCustomIconCtrlUsed)?
+ custom_btn_params:normal_btn_params;
p.name(std::string(child->getName()) + " tab");
p.rect(btn_rect);
p.click_callback.function(boost::bind(&LLTabContainer::onTabBtn, this, _2, child));
@@ -980,7 +1097,14 @@ void LLTabContainer::addTabPanel(const TabPanelParams& panel)
p.follows.flags = p.follows.flags() | FOLLOWS_BOTTOM;
}
-++ btn = LLUICtrlFactory::create<LLButton>(p);
+ if(mCustomIconCtrlUsed)
+ {
+ btn = LLUICtrlFactory::create<LLCustomButtonIconCtrl>(custom_btn_params);
+ }
+ else
+ {
+ btn = LLUICtrlFactory::create<LLButton>(p);
+ }
}
}
@@ -1373,6 +1497,8 @@ BOOL LLTabContainer::setTab(S32 which)
{
LLTabTuple* tuple = *iter;
BOOL is_selected = ( tuple == selected_tuple );
+ tuple->mButton->setUseEllipses(mUseTabEllipses);
+ tuple->mButton->setHAlign(mFontHalign);
tuple->mTabPanel->setVisible( is_selected );
// tuple->mTabPanel->setFocus(is_selected); // not clear that we want to do this here.
tuple->mButton->setToggleState( is_selected );
@@ -1478,32 +1604,71 @@ void LLTabContainer::setTabPanelFlashing(LLPanel* child, BOOL state )
void LLTabContainer::setTabImage(LLPanel* child, std::string image_name, const LLColor4& color)
{
- static LLUICachedControl<S32> tab_padding ("UITabPadding", 0);
LLTabTuple* tuple = getTabByPanel(child);
if( tuple )
{
- tuple->mButton->setImageOverlay(image_name, LLFontGL::RIGHT, color);
+ tuple->mButton->setImageOverlay(image_name, LLFontGL::LEFT, color);
+ reshapeTuple(tuple);
+ }
+}
+
+void LLTabContainer::setTabImage(LLPanel* child, const LLUUID& image_id, const LLColor4& color)
+{
+ LLTabTuple* tuple = getTabByPanel(child);
+ if( tuple )
+ {
+ tuple->mButton->setImageOverlay(image_id, LLFontGL::LEFT, color);
+ reshapeTuple(tuple);
+ }
+}
- if (!mIsVertical)
- {
- // remove current width from total tab strip width
- mTotalTabWidth -= tuple->mButton->getRect().getWidth();
+void LLTabContainer::setTabImage(LLPanel* child, LLIconCtrl* icon)
+{
+ LLTabTuple* tuple = getTabByPanel(child);
+ LLCustomButtonIconCtrl* button;
- S32 image_overlay_width = tuple->mButton->getImageOverlay().notNull() ?
- tuple->mButton->getImageOverlay()->getImage()->getWidth(0) :
- 0;
+ if(tuple)
+ {
+ button = dynamic_cast<LLCustomButtonIconCtrl*>(tuple->mButton);
+ if(button)
+ {
+ button->setIcon(icon);
+ reshapeTuple(tuple);
+ }
+ }
+}
- tuple->mPadding = image_overlay_width;
+void LLTabContainer::reshapeTuple(LLTabTuple* tuple)
+{
+ static LLUICachedControl<S32> tab_padding ("UITabPadding", 0);
- tuple->mButton->setRightHPad(6);
- tuple->mButton->reshape(llclamp(mFont->getWidth(tuple->mButton->getLabelSelected()) + tab_padding + tuple->mPadding, mMinTabWidth, mMaxTabWidth),
- tuple->mButton->getRect().getHeight());
- // add back in button width to total tab strip width
- mTotalTabWidth += tuple->mButton->getRect().getWidth();
+ if (!mIsVertical)
+ {
+ S32 image_overlay_width = 0;
- // tabs have changed size, might need to scroll to see current tab
- updateMaxScrollPos();
+ if(mCustomIconCtrlUsed)
+ {
+ LLCustomButtonIconCtrl* button = dynamic_cast<LLCustomButtonIconCtrl*>(tuple->mButton);
+ LLIconCtrl* icon_ctrl = button ? button->getIconCtrl() : NULL;
+ image_overlay_width = icon_ctrl ? icon_ctrl->getRect().getWidth() : 0;
+ }
+ else
+ {
+ image_overlay_width = tuple->mButton->getImageOverlay().notNull() ?
+ tuple->mButton->getImageOverlay()->getImage()->getWidth(0) : 0;
}
+ // remove current width from total tab strip width
+ mTotalTabWidth -= tuple->mButton->getRect().getWidth();
+
+ tuple->mPadding = image_overlay_width;
+
+ tuple->mButton->reshape(llclamp(mFont->getWidth(tuple->mButton->getLabelSelected()) + tab_padding + tuple->mPadding, mMinTabWidth, mMaxTabWidth),
+ tuple->mButton->getRect().getHeight());
+ // add back in button width to total tab strip width
+ mTotalTabWidth += tuple->mButton->getRect().getWidth();
+
+ // tabs have changed size, might need to scroll to see current tab
+ updateMaxScrollPos();
}
}
@@ -1566,7 +1731,10 @@ void LLTabContainer::onTabBtn( const LLSD& data, LLPanel* panel )
LLTabTuple* tuple = getTabByPanel(panel);
selectTabPanel( panel );
- tuple->mTabPanel->setFocus(TRUE);
+ if (tuple)
+ {
+ tuple->mTabPanel->setFocus(TRUE);
+ }
}
void LLTabContainer::onNextBtn( const LLSD& data )
diff --git a/indra/llui/lltabcontainer.h b/indra/llui/lltabcontainer.h
index 5d0f194bf9..50ec2679f6 100644
--- a/indra/llui/lltabcontainer.h
+++ b/indra/llui/lltabcontainer.h
@@ -36,6 +36,8 @@
#include "llpanel.h"
#include "lltextbox.h"
#include "llframetimer.h"
+#include "lliconctrl.h"
+#include "llbutton.h"
class LLTabTuple;
@@ -90,6 +92,26 @@ public:
middle_tab,
last_tab;
+ /**
+ * Tab label horizontal alignment
+ */
+ Optional<LLFontGL::HAlign> font_halign;
+
+ /**
+ * Tab label ellipses
+ */
+ Optional<bool> use_ellipses;
+
+ /**
+ * Use LLCustomButtonIconCtrl or LLButton in LLTabTuple
+ */
+ Optional<bool> use_custom_icon_ctrl;
+
+ /**
+ * Paddings for LLIconCtrl in case of LLCustomButtonIconCtrl usage(use_custom_icon_ctrl = true)
+ */
+ Optional<S32> tab_icon_ctrl_pad;
+
Params();
};
@@ -172,6 +194,8 @@ public:
BOOL getTabPanelFlashing(LLPanel* child);
void setTabPanelFlashing(LLPanel* child, BOOL state);
void setTabImage(LLPanel* child, std::string img_name, const LLColor4& color = LLColor4::white);
+ void setTabImage(LLPanel* child, const LLUUID& img_id, const LLColor4& color = LLColor4::white);
+ void setTabImage(LLPanel* child, LLIconCtrl* icon);
void setTitle( const std::string& title );
const std::string getPanelTitle(S32 index);
@@ -227,6 +251,7 @@ private:
// updates tab button images given the tuple, tab position and the corresponding params
void update_images(LLTabTuple* tuple, TabParams params, LLTabContainer::TabPosition pos);
+ void reshapeTuple(LLTabTuple* tuple);
// Variables
@@ -276,6 +301,10 @@ private:
TabParams mFirstTabParams;
TabParams mMiddleTabParams;
TabParams mLastTabParams;
+
+ bool mCustomIconCtrlUsed;
+ S32 mTabIconCtrlPad;
+ bool mUseTabEllipses;
};
#endif // LL_TABCONTAINER_H
diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp
index 17aecaf32f..390ec234d3 100644
--- a/indra/llui/lltextbase.cpp
+++ b/indra/llui/lltextbase.cpp
@@ -185,7 +185,7 @@ LLTextBase::LLTextBase(const LLTextBase::Params &p)
mWriteableBgColor(p.bg_writeable_color),
mReadOnlyBgColor(p.bg_readonly_color),
mFocusBgColor(p.bg_focus_color),
- mReflowNeeded(FALSE),
+ mReflowIndex(S32_MAX),
mCursorPos( 0 ),
mScrollNeeded(FALSE),
mDesiredXPixel(-1),
@@ -244,8 +244,9 @@ LLTextBase::LLTextBase(const LLTextBase::Params &p)
LLTextBase::~LLTextBase()
{
- delete mPopupMenu;
- clearSegments();
+ // Menu, like any other LLUICtrl, is deleted by its parent - gMenuHolder
+
+ mSegments.clear();
}
void LLTextBase::initFromParams(const LLTextBase::Params& p)
@@ -291,9 +292,13 @@ bool LLTextBase::truncate()
return did_truncate;
}
-LLStyle::Params LLTextBase::getDefaultStyle()
+LLStyle::Params LLTextBase::getDefaultStyleParams()
{
- return LLStyle::Params().color(mFgColor.get()).readonly_color(mReadOnlyFgColor.get()).font(mDefaultFont).drop_shadow(mFontShadow);
+ return LLStyle::Params()
+ .color(LLUIColor(&mFgColor))
+ .readonly_color(LLUIColor(&mReadOnlyFgColor))
+ .font(mDefaultFont)
+ .drop_shadow(mFontShadow);
}
void LLTextBase::onValueChange(S32 start, S32 end)
@@ -307,7 +312,6 @@ void LLTextBase::drawSelectionBackground()
// Draw selection even if we don't have keyboard focus for search/replace
if( hasSelection() && !mLineInfoList.empty())
{
- LLWString text = getWText();
std::vector<LLRect> selection_rects;
S32 selection_left = llmin( mSelectionStart, mSelectionEnd );
@@ -346,6 +350,8 @@ void LLTextBase::drawSelectionBackground()
S32 segment_line_start = segmentp->getStart() + segment_offset;
S32 segment_line_end = llmin(segmentp->getEnd(), line_iter->mDocIndexEnd);
+ if (segment_line_start > segment_line_end) break;
+
S32 segment_width = 0;
S32 segment_height = 0;
@@ -357,8 +363,11 @@ void LLTextBase::drawSelectionBackground()
selection_rect.mLeft += segment_width;
}
- // if selection spans end of current segment...
- if (selection_right > segment_line_end)
+ // if selection_right == segment_line_end then that means we are the first character of the next segment
+ // or first character of the next line, in either case we want to add the length of the current segment
+ // to the selection rectangle and continue.
+ // if selection right > segment_line_end then selection spans end of current segment...
+ if (selection_right >= segment_line_end)
{
// extend selection slightly beyond end of line
// to indicate selection of newline character (use "n" character to determine width)
@@ -406,7 +415,7 @@ void LLTextBase::drawCursor()
&& gFocusMgr.getAppHasFocus()
&& !mReadOnly)
{
- LLWString wtext = getWText();
+ const LLWString &wtext = getWText();
const llwchar* text = wtext.c_str();
LLRect cursor_rect = getLocalRectFromDocIndex(mCursorPos);
@@ -492,7 +501,6 @@ void LLTextBase::drawCursor()
void LLTextBase::drawText()
{
- LLWString text = getWText();
const S32 text_len = getLength();
if( text_len <= 0 )
{
@@ -619,7 +627,8 @@ S32 LLTextBase::insertStringNoUndo(S32 pos, const LLWString &wstr, LLTextBase::s
else
{
// create default editable segment to hold new text
- default_segment = new LLNormalTextSegment( new LLStyle(getDefaultStyle()), pos, pos + insert_len, *this);
+ LLStyleConstSP sp(new LLStyle(getDefaultStyleParams()));
+ default_segment = new LLNormalTextSegment( sp, pos, pos + insert_len, *this);
}
// shift remaining segments to right
@@ -656,7 +665,7 @@ S32 LLTextBase::insertStringNoUndo(S32 pos, const LLWString &wstr, LLTextBase::s
}
onValueChange(pos, pos + insert_len);
- needsReflow();
+ needsReflow(pos);
return insert_len;
}
@@ -716,7 +725,7 @@ S32 LLTextBase::removeStringNoUndo(S32 pos, S32 length)
createDefaultSegment();
onValueChange(pos, pos);
- needsReflow();
+ needsReflow(pos);
return -length; // This will be wrong if someone calls removeStringNoUndo with an excessive length
}
@@ -732,7 +741,7 @@ S32 LLTextBase::overwriteCharNoUndo(S32 pos, llwchar wc)
getViewModel()->setDisplay(text);
onValueChange(pos, pos + 1);
- needsReflow();
+ needsReflow(pos);
return 1;
}
@@ -743,7 +752,8 @@ void LLTextBase::createDefaultSegment()
// ensures that there is always at least one segment
if (mSegments.empty())
{
- LLTextSegmentPtr default_segment = new LLNormalTextSegment( new LLStyle(getDefaultStyle()), 0, getLength() + 1, *this);
+ LLStyleConstSP sp(new LLStyle(getDefaultStyleParams()));
+ LLTextSegmentPtr default_segment = new LLNormalTextSegment( sp, 0, getLength() + 1, *this);
mSegments.insert(default_segment);
default_segment->linkToDocument(this);
}
@@ -757,15 +767,18 @@ void LLTextBase::insertSegment(LLTextSegmentPtr segment_to_insert)
}
segment_set_t::iterator cur_seg_iter = getSegIterContaining(segment_to_insert->getStart());
+ S32 reflow_start_index = 0;
if (cur_seg_iter == mSegments.end())
{
mSegments.insert(segment_to_insert);
segment_to_insert->linkToDocument(this);
+ reflow_start_index = segment_to_insert->getStart();
}
else
{
LLTextSegmentPtr cur_segmentp = *cur_seg_iter;
+ reflow_start_index = cur_segmentp->getStart();
if (cur_segmentp->getStart() < segment_to_insert->getStart())
{
S32 old_segment_end = cur_segmentp->getEnd();
@@ -773,7 +786,8 @@ void LLTextBase::insertSegment(LLTextSegmentPtr segment_to_insert)
cur_segmentp->setEnd(segment_to_insert->getStart());
// advance to next segment
// insert remainder of old segment
- LLTextSegmentPtr remainder_segment = new LLNormalTextSegment( cur_segmentp->getStyle(), segment_to_insert->getStart(), old_segment_end, *this);
+ LLStyleConstSP sp = cur_segmentp->getStyle();
+ LLTextSegmentPtr remainder_segment = new LLNormalTextSegment( sp, segment_to_insert->getStart(), old_segment_end, *this);
mSegments.insert(cur_seg_iter, remainder_segment);
remainder_segment->linkToDocument(this);
// insert new segment before remainder of old segment
@@ -823,7 +837,7 @@ void LLTextBase::insertSegment(LLTextSegmentPtr segment_to_insert)
}
// layout potentially changed
- needsReflow();
+ needsReflow(reflow_start_index);
}
BOOL LLTextBase::handleMouseDown(S32 x, S32 y, MASK mask)
@@ -948,7 +962,18 @@ void LLTextBase::reshape(S32 width, S32 height, BOOL called_from_parent)
{
if (width != getRect().getWidth() || height != getRect().getHeight())
{
+ //EXT-4288
+ //to keep consistance scrolling behaviour
+ //when scrolling from top and from bottom...
+ bool is_scrolled_to_end = (mScroller!=NULL) && scrolledToEnd();
+
LLUICtrl::reshape( width, height, called_from_parent );
+
+ if (is_scrolled_to_end)
+ {
+ deselect();
+ endOfDoc();
+ }
// do this first after reshape, because other things depend on
// up-to-date mVisibleTextRect
@@ -1018,12 +1043,29 @@ void LLTextBase::setReadOnlyColor(const LLColor4 &c)
}
//virtual
+void LLTextBase::handleVisibilityChange( BOOL new_visibility )
+{
+ if(!new_visibility && mPopupMenu)
+ {
+ mPopupMenu->hide();
+ }
+ LLUICtrl::handleVisibilityChange(new_visibility);
+}
+
+//virtual
void LLTextBase::setValue(const LLSD& value )
{
setText(value.asString());
}
//virtual
+BOOL LLTextBase::canDeselect() const
+{
+ return hasSelection();
+}
+
+
+//virtual
void LLTextBase::deselect()
{
mSelectionStart = 0;
@@ -1068,15 +1110,16 @@ S32 LLTextBase::getLeftOffset(S32 width)
static LLFastTimer::DeclareTimer FTM_TEXT_REFLOW ("Text Reflow");
-void LLTextBase::reflow(S32 start_index)
+void LLTextBase::reflow()
{
LLFastTimer ft(FTM_TEXT_REFLOW);
updateSegments();
- while(mReflowNeeded)
+ while(mReflowIndex < S32_MAX)
{
- mReflowNeeded = false;
+ S32 start_index = mReflowIndex;
+ mReflowIndex = S32_MAX;
// shrink document to minimum size (visible portion of text widget)
// to force inlined widgets with follows set to shrink
@@ -1108,7 +1151,6 @@ void LLTextBase::reflow(S32 start_index)
S32 line_start_index = 0;
const S32 text_available_width = mVisibleTextRect.getWidth() - mHPad; // reserve room for margin
S32 remaining_pixels = text_available_width;
- LLWString text(getWText());
S32 line_count = 0;
// find and erase line info structs starting at start_index and going to end of document
@@ -1118,6 +1160,7 @@ void LLTextBase::reflow(S32 start_index)
line_list_t::iterator iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), start_index, line_end_compare());
line_start_index = iter->mDocIndexStart;
line_count = iter->mLineNum;
+ cur_top = iter->mRect.mTop;
getSegmentAndOffset(iter->mDocIndexStart, &seg_iter, &seg_offset);
mLineInfoList.erase(iter, mLineInfoList.end());
}
@@ -1425,10 +1468,10 @@ LLTextBase::segment_set_t::const_iterator LLTextBase::getSegIterContaining(S32 i
}
// Finds the text segment (if any) at the give local screen position
-LLTextSegmentPtr LLTextBase::getSegmentAtLocalPos( S32 x, S32 y )
+LLTextSegmentPtr LLTextBase::getSegmentAtLocalPos( S32 x, S32 y, bool hit_past_end_of_line)
{
// Find the cursor position at the requested local screen position
- S32 offset = getDocIndexFromLocalCoord( x, y, FALSE );
+ S32 offset = getDocIndexFromLocalCoord( x, y, FALSE, hit_past_end_of_line);
segment_set_t::iterator seg_iter = getSegIterContaining(offset);
if (seg_iter != mSegments.end())
{
@@ -1464,6 +1507,7 @@ void LLTextBase::createUrlContextMenu(S32 x, S32 y, const std::string &in_url)
registrar.add("Url.OpenExternal", boost::bind(&LLUrlAction::openURLExternal, 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.ShowOnMap", boost::bind(&LLUrlAction::showLocationOnMap, url));
registrar.add("Url.CopyLabel", boost::bind(&LLUrlAction::copyLabelToClipboard, url));
registrar.add("Url.CopyUrl", boost::bind(&LLUrlAction::copyURLToClipboard, url));
@@ -1510,16 +1554,7 @@ std::string LLTextBase::getText() const
void LLTextBase::appendText(const std::string &new_text, bool prepend_newline, const LLStyle::Params& input_params)
{
LLStyle::Params style_params(input_params);
- style_params.fillFrom(getDefaultStyle());
-
- if (!style_params.font.isProvided())
- {
- style_params.font = mDefaultFont;
- }
- if (!style_params.drop_shadow.isProvided())
- {
- style_params.drop_shadow = mFontShadow;
- }
+ style_params.fillFrom(getDefaultStyleParams());
S32 part = (S32)LLTextParser::WHOLE;
if(mParseHTML)
@@ -1536,13 +1571,7 @@ void LLTextBase::appendText(const std::string &new_text, bool prepend_newline, c
LLStyle::Params link_params = style_params;
link_params.color = match.getColor();
link_params.readonly_color = match.getColor();
- // apply font name from requested style_params
- std::string font_name = LLFontGL::nameFromFont(style_params.font());
- std::string font_size = LLFontGL::sizeFromFont(style_params.font());
- link_params.font.name(font_name);
- link_params.font.size(font_size);
link_params.font.style("UNDERLINE");
-
link_params.link_href = match.getUrl();
// output the text before the Url
@@ -1570,25 +1599,35 @@ void LLTextBase::appendText(const std::string &new_text, bool prepend_newline, c
{
LLStyle::Params icon;
icon.image = image;
- // HACK: fix spacing of images and remove the fixed char spacing
- appendAndHighlightText(" ", prepend_newline, part, icon);
+ // Text will be replaced during rendering with the icon,
+ // but string cannot be empty or the segment won't be
+ // added (or drawn).
+ appendAndHighlightText(" ", prepend_newline, part, icon);
prepend_newline = false;
}
}
- // output the styled Url
- appendAndHighlightText(match.getLabel(), prepend_newline, part, link_params);
- prepend_newline = false;
- // set the tooltip for the Url label
- if (! match.getTooltip().empty())
+ // output the styled Url (unless we've been asked to suppress hyperlinking)
+ if (match.isLinkDisabled())
+ {
+ appendAndHighlightText(match.getLabel(), prepend_newline, part, style_params);
+ }
+ else
{
- segment_set_t::iterator it = getSegIterContaining(getLength()-1);
- if (it != mSegments.end())
+ appendAndHighlightText(match.getLabel(), prepend_newline, part, link_params);
+
+ // set the tooltip for the Url label
+ if (! match.getTooltip().empty())
{
- LLTextSegmentPtr segment = *it;
- segment->setToolTip(match.getTooltip());
+ segment_set_t::iterator it = getSegIterContaining(getLength()-1);
+ if (it != mSegments.end())
+ {
+ LLTextSegmentPtr segment = *it;
+ segment->setToolTip(match.getTooltip());
+ }
}
}
+ prepend_newline = false;
// move on to the rest of the text after the Url
if (end < (S32)text.length())
@@ -1611,9 +1650,15 @@ void LLTextBase::appendText(const std::string &new_text, bool prepend_newline, c
}
}
-void LLTextBase::appendAndHighlightText(const std::string &new_text, bool prepend_newline, S32 highlight_part, const LLStyle::Params& stylep)
+void LLTextBase::needsReflow(S32 index)
+{
+ lldebugs << "reflow on object " << (void*)this << " index = " << mReflowIndex << ", new index = " << index << llendl;
+ mReflowIndex = llmin(mReflowIndex, index);
+}
+
+void LLTextBase::appendAndHighlightText(const std::string &new_text, bool prepend_newline, S32 highlight_part, const LLStyle::Params& style_params)
{
- if (new_text.empty()) return;
+ if (new_text.empty()) return;
// Save old state
S32 selection_start = mSelectionStart;
@@ -1631,7 +1676,7 @@ void LLTextBase::appendAndHighlightText(const std::string &new_text, bool prepen
if (mParseHighlights && highlight)
{
- LLStyle::Params highlight_params = stylep;
+ LLStyle::Params highlight_params(style_params);
LLSD pieces = highlight->parsePartialLineHighlights(new_text, highlight_params.color(), (LLTextParser::EHighlightPosition)highlight_part);
for (S32 i = 0; i < pieces.size(); i++)
@@ -1651,7 +1696,8 @@ void LLTextBase::appendAndHighlightText(const std::string &new_text, bool prepen
wide_text = utf8str_to_wstring(pieces[i]["text"].asString());
}
S32 cur_length = getLength();
- LLTextSegmentPtr segmentp = new LLNormalTextSegment(new LLStyle(highlight_params), cur_length, cur_length + wide_text.size(), *this);
+ LLStyleConstSP sp(new LLStyle(highlight_params));
+ LLTextSegmentPtr segmentp = new LLNormalTextSegment(sp, cur_length, cur_length + wide_text.size(), *this);
segment_vec_t segments;
segments.push_back(segmentp);
insertStringNoUndo(cur_length, wide_text, &segments);
@@ -1675,7 +1721,8 @@ void LLTextBase::appendAndHighlightText(const std::string &new_text, bool prepen
segment_vec_t segments;
S32 segment_start = old_length;
S32 segment_end = old_length + wide_text.size();
- segments.push_back(new LLNormalTextSegment(new LLStyle(stylep), segment_start, segment_end, *this ));
+ LLStyleConstSP sp(new LLStyle(style_params));
+ segments.push_back(new LLNormalTextSegment(sp, segment_start, segment_end, *this ));
insertStringNoUndo(getLength(), wide_text, &segments);
}
@@ -1719,7 +1766,7 @@ void LLTextBase::replaceUrlLabel(const std::string &url,
for (it = mSegments.begin(); it != mSegments.end(); ++it)
{
LLTextSegment *seg = *it;
- const LLStyleSP style = seg->getStyle();
+ LLStyleConstSP style = seg->getStyle();
// update segment start/end length in case we replaced text earlier
S32 seg_length = seg->getEnd() - seg->getStart();
@@ -1756,7 +1803,7 @@ void LLTextBase::setWText(const LLWString& text)
setText(wstring_to_utf8str(text));
}
-LLWString LLTextBase::getWText() const
+const LLWString& LLTextBase::getWText() const
{
return getViewModel()->getDisplay();
}
@@ -1765,7 +1812,7 @@ LLWString LLTextBase::getWText() const
// will be put to its right. If round is false, the cursor will always be put to the
// character's left.
-S32 LLTextBase::getDocIndexFromLocalCoord( S32 local_x, S32 local_y, BOOL round ) const
+S32 LLTextBase::getDocIndexFromLocalCoord( S32 local_x, S32 local_y, BOOL round, bool hit_past_end_of_line) const
{
// Figure out which line we're nearest to.
LLRect visible_region = getVisibleDocumentRect();
@@ -1790,11 +1837,18 @@ S32 LLTextBase::getDocIndexFromLocalCoord( S32 local_x, S32 local_y, BOOL round
const LLTextSegmentPtr segmentp = *line_seg_iter;
S32 segment_line_start = segmentp->getStart() + line_seg_offset;
- S32 segment_line_length = llmin(segmentp->getEnd(), line_iter->mDocIndexEnd - 1) - segment_line_start;
+ S32 segment_line_length = llmin(segmentp->getEnd(), line_iter->mDocIndexEnd) - segment_line_start;
S32 text_width, text_height;
- segmentp->getDimensions(line_seg_offset, segment_line_length, text_width, text_height);
- if (local_x < start_x + text_width // cursor to left of right edge of text
- || segmentp->getEnd() >= line_iter->mDocIndexEnd - 1) // or this segment wraps to next line
+ bool newline = segmentp->getDimensions(line_seg_offset, segment_line_length, text_width, text_height);
+
+ // if we've reached a line of text *below* the mouse cursor, doc index is first character on that line
+ if (hit_past_end_of_line && local_y - mVisibleTextRect.mBottom + visible_region.mBottom > line_iter->mRect.mTop)
+ {
+ pos = segment_line_start;
+ break;
+ }
+ if (local_x < start_x + text_width // cursor to left of right edge of text
+ || newline) // or this line ends with a newline, set doc pos to newline char
{
// Figure out which character we're nearest to.
S32 offset;
@@ -1818,6 +1872,13 @@ S32 LLTextBase::getDocIndexFromLocalCoord( S32 local_x, S32 local_y, BOOL round
pos = segment_line_start + offset;
break;
}
+ else if (hit_past_end_of_line && segmentp->getEnd() >= line_iter->mDocIndexEnd - 1)
+ {
+ // segment wraps to next line, so just set doc pos to start of next line (represented by mDocIndexEnd)
+ pos = llmin(getLength(), line_iter->mDocIndexEnd);
+ break;
+ }
+
start_x += text_width;
}
@@ -2212,9 +2273,9 @@ bool LLTextSegment::canEdit() const { return false; }
void LLTextSegment::unlinkFromDocument(LLTextBase*) {}
void LLTextSegment::linkToDocument(LLTextBase*) {}
const LLColor4& LLTextSegment::getColor() const { return LLColor4::white; }
-void LLTextSegment::setColor(const LLColor4 &color) {}
-const LLStyleSP LLTextSegment::getStyle() const {static LLStyleSP sp(new LLStyle()); return sp; }
-void LLTextSegment::setStyle(const LLStyleSP &style) {}
+//void LLTextSegment::setColor(const LLColor4 &color) {}
+LLStyleConstSP LLTextSegment::getStyle() const {static LLStyleConstSP sp(new LLStyle()); return sp; }
+void LLTextSegment::setStyle(LLStyleConstSP style) {}
void LLTextSegment::setToken( LLKeywordToken* token ) {}
LLKeywordToken* LLTextSegment::getToken() const { return NULL; }
void LLTextSegment::setToolTip( const std::string &msg ) {}
@@ -2239,7 +2300,7 @@ BOOL LLTextSegment::hasMouseCapture() { return FALSE; }
// LLNormalTextSegment
//
-LLNormalTextSegment::LLNormalTextSegment( const LLStyleSP& style, S32 start, S32 end, LLTextBase& editor )
+LLNormalTextSegment::LLNormalTextSegment( LLStyleConstSP style, S32 start, S32 end, LLTextBase& editor )
: LLTextSegment(start, end),
mStyle( style ),
mToken(NULL),
@@ -2250,7 +2311,7 @@ LLNormalTextSegment::LLNormalTextSegment( const LLStyleSP& style, S32 start, S32
LLUIImagePtr image = mStyle->getImage();
if (image.notNull())
{
- mImageLoadedConnection = image->addLoadedCallback(boost::bind(&LLTextBase::needsReflow, &mEditor));
+ mImageLoadedConnection = image->addLoadedCallback(boost::bind(&LLTextBase::needsReflow, &mEditor, start));
}
}
@@ -2276,14 +2337,21 @@ F32 LLNormalTextSegment::draw(S32 start, S32 end, S32 selection_start, S32 selec
{
if ( mStyle->isImage() && (start >= 0) && (end <= mEnd - mStart))
{
+ // ...for images, only render the image, not the underlying text,
+ // which is only a placeholder space
LLColor4 color = LLColor4::white % mEditor.getDrawContext().mAlpha;
LLUIImagePtr image = mStyle->getImage();
S32 style_image_height = image->getHeight();
S32 style_image_width = image->getWidth();
- // Center the image vertically
- S32 image_bottom = draw_rect.getCenterY() - (style_image_height/2);
+ // Text is drawn from the top of the draw_rect downward
+ S32 text_center = draw_rect.mTop - (mFontHeight / 2);
+ // Align image to center of text
+ S32 image_bottom = text_center - (style_image_height / 2);
image->draw(draw_rect.mLeft, image_bottom,
style_image_width, style_image_height, color);
+
+ const S32 IMAGE_HPAD = 3;
+ return draw_rect.mLeft + style_image_width + IMAGE_HPAD;
}
return drawClippedSegment( getStart() + start, getStart() + end, selection_start, selection_end, draw_rect);
@@ -2313,8 +2381,6 @@ F32 LLNormalTextSegment::drawClippedSegment(S32 seg_start, S32 seg_end, S32 sele
LLColor4 color = (mEditor.getReadOnly() ? mStyle->getReadOnlyColor() : mStyle->getColor()) % alpha;
- font = mStyle->getFont();
-
if( selection_start > seg_start )
{
// Draw normally
@@ -2374,8 +2440,12 @@ BOOL LLNormalTextSegment::handleHover(S32 x, S32 y, MASK mask)
{
if (getStyle() && getStyle()->isLink())
{
- LLUI::getWindow()->setCursor(UI_CURSOR_HAND);
- return TRUE;
+ // Only process the click if it's actually in this segment, not to the right of the end-of-line.
+ if(mEditor.getSegmentAtLocalPos(x, y, false) == this)
+ {
+ LLUI::getWindow()->setCursor(UI_CURSOR_HAND);
+ return TRUE;
+ }
}
return FALSE;
}
@@ -2384,8 +2454,12 @@ BOOL LLNormalTextSegment::handleRightMouseDown(S32 x, S32 y, MASK mask)
{
if (getStyle() && getStyle()->isLink())
{
- mEditor.createUrlContextMenu(x, y, getStyle()->getLinkHREF());
- return TRUE;
+ // Only process the click if it's actually in this segment, not to the right of the end-of-line.
+ if(mEditor.getSegmentAtLocalPos(x, y, false) == this)
+ {
+ mEditor.createUrlContextMenu(x, y, getStyle()->getLinkHREF());
+ return TRUE;
+ }
}
return FALSE;
}
@@ -2394,8 +2468,12 @@ BOOL LLNormalTextSegment::handleMouseDown(S32 x, S32 y, MASK mask)
{
if (getStyle() && getStyle()->isLink())
{
- // eat mouse down event on hyperlinks, so we get the mouse up
- return TRUE;
+ // Only process the click if it's actually in this segment, not to the right of the end-of-line.
+ if(mEditor.getSegmentAtLocalPos(x, y, false) == this)
+ {
+ // eat mouse down event on hyperlinks, so we get the mouse up
+ return TRUE;
+ }
}
return FALSE;
@@ -2405,8 +2483,12 @@ BOOL LLNormalTextSegment::handleMouseUp(S32 x, S32 y, MASK mask)
{
if (getStyle() && getStyle()->isLink())
{
- LLUrlAction::clickAction(getStyle()->getLinkHREF());
- return TRUE;
+ // Only process the click if it's actually in this segment, not to the right of the end-of-line.
+ if(mEditor.getSegmentAtLocalPos(x, y, false) == this)
+ {
+ LLUrlAction::clickAction(getStyle()->getLinkHREF());
+ return TRUE;
+ }
}
return FALSE;
@@ -2451,7 +2533,7 @@ bool LLNormalTextSegment::getDimensions(S32 first_char, S32 num_chars, S32& widt
if (num_chars > 0)
{
height = mFontHeight;
- LLWString text = mEditor.getWText();
+ const LLWString &text = mEditor.getWText();
// if last character is a newline, then return true, forcing line break
llwchar last_char = text[mStart + first_char + num_chars - 1];
if (last_char == '\n')
@@ -2478,7 +2560,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
{
- LLWString text = mEditor.getWText();
+ const LLWString &text = mEditor.getWText();
return mStyle->getFont()->charFromPixelOffset(text.c_str(), mStart + start_offset,
(F32)segment_local_x_coord,
F32_MAX,
@@ -2488,12 +2570,12 @@ 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
{
- LLWString text = mEditor.getWText();
+ const LLWString &text = mEditor.getWText();
LLUIImagePtr image = mStyle->getImage();
if( image.notNull())
{
- num_pixels -= image->getWidth();
+ num_pixels = llmax(0, num_pixels - image->getWidth());
}
// search for newline and if found, truncate there
diff --git a/indra/llui/lltextbase.h b/indra/llui/lltextbase.h
index 038b9eaa62..8ed0680df9 100644
--- a/indra/llui/lltextbase.h
+++ b/indra/llui/lltextbase.h
@@ -41,12 +41,14 @@
#include "llpanel.h"
#include <string>
+#include <vector>
#include <set>
#include <boost/signals2.hpp>
class LLContextMenu;
class LLTextSegment;
+class LLNormalTextSegment;
typedef LLPointer<LLTextSegment> LLTextSegmentPtr;
@@ -60,6 +62,9 @@ class LLTextBase
protected LLEditMenuHandler
{
public:
+ friend class LLTextSegment;
+ friend class LLNormalTextSegment;
+
struct LineSpacingParams : public LLInitParam::Choice<LineSpacingParams>
{
Alternative<F32> multiple;
@@ -121,11 +126,13 @@ public:
/*virtual*/ BOOL acceptsTextInput() const { return !mReadOnly; }
/*virtual*/ void setColor( const LLColor4& c );
virtual void setReadOnlyColor(const LLColor4 &c);
+ virtual void handleVisibilityChange( BOOL new_visibility );
/*virtual*/ void setValue(const LLSD& value );
/*virtual*/ LLTextViewModel* getViewModel() const;
// LLEditMenuHandler interface
+ /*virtual*/ BOOL canDeselect() const;
/*virtual*/ void deselect();
// used by LLTextSegment layout code
@@ -144,11 +151,11 @@ public:
// wide-char versions
void setWText(const LLWString& text);
- LLWString getWText() const;
+ const LLWString& getWText() const;
void appendText(const std::string &new_text, bool prepend_newline, const LLStyle::Params& input_params = LLStyle::Params());
// force reflow of text
- void needsReflow() { mReflowNeeded = TRUE; }
+ void needsReflow(S32 index = 0);
S32 getLength() const { return getWText().length(); }
S32 getLineCount() const { return mLineInfoList.size(); }
@@ -163,7 +170,7 @@ public:
S32 getVPad() { return mVPad; }
S32 getHPad() { return mHPad; }
- S32 getDocIndexFromLocalCoord( S32 local_x, S32 local_y, BOOL round ) const;
+ S32 getDocIndexFromLocalCoord( S32 local_x, S32 local_y, BOOL round, bool hit_past_end_of_line = true) const;
LLRect getLocalRectFromDocIndex(S32 pos) const;
LLRect getDocRectFromDocIndex(S32 pos) const;
@@ -184,7 +191,6 @@ public:
bool scrolledToEnd();
const LLFontGL* getDefaultFont() const { return mDefaultFont; }
- LLStyle::Params getDefaultStyle();
public:
// Fired when a URL link is clicked
@@ -274,14 +280,15 @@ protected:
// manage segments
void getSegmentAndOffset( S32 startpos, segment_set_t::const_iterator* seg_iter, S32* offsetp ) const;
void getSegmentAndOffset( S32 startpos, segment_set_t::iterator* seg_iter, S32* offsetp );
- LLTextSegmentPtr getSegmentAtLocalPos( S32 x, S32 y );
+ LLTextSegmentPtr getSegmentAtLocalPos( S32 x, S32 y, bool hit_past_end_of_line = true);
segment_set_t::iterator getSegIterContaining(S32 index);
segment_set_t::const_iterator getSegIterContaining(S32 index) const;
void clearSegments();
void createDefaultSegment();
virtual void updateSegments();
void insertSegment(LLTextSegmentPtr segment_to_insert);
-
+ LLStyle::Params getDefaultStyleParams();
+
// manage lines
S32 getLineStart( S32 line ) const;
S32 getLineEnd( S32 line ) const;
@@ -290,7 +297,7 @@ protected:
S32 getFirstVisibleLine() const;
std::pair<S32, S32> getVisibleLines(bool fully_visible = false);
S32 getLeftOffset(S32 width);
- void reflow(S32 start_index = 0);
+ void reflow();
// cursor
void updateCursorXPos();
@@ -360,7 +367,7 @@ protected:
class LLScrollContainer* mScroller;
// transient state
- bool mReflowNeeded; // need to reflow text because of change to text contents or display region
+ S32 mReflowIndex; // index at which to start reflow. S32_MAX indicates no reflow needed.
bool mScrollNeeded; // need to change scroll region because of change to cursor position
S32 mScrollIndex; // index of first character to keep visible in scroll region
@@ -388,9 +395,9 @@ public:
virtual void linkToDocument(class LLTextBase* editor);
virtual const LLColor4& getColor() const;
- virtual void setColor(const LLColor4 &color);
- virtual const LLStyleSP getStyle() const;
- virtual void setStyle(const LLStyleSP &style);
+ //virtual void setColor(const LLColor4 &color);
+ virtual LLStyleConstSP getStyle() const;
+ virtual void setStyle(LLStyleConstSP style);
virtual void setToken( LLKeywordToken* token );
virtual LLKeywordToken* getToken() const;
virtual void setToolTip(const std::string& tooltip);
@@ -426,7 +433,7 @@ protected:
class LLNormalTextSegment : public LLTextSegment
{
public:
- LLNormalTextSegment( const LLStyleSP& style, S32 start, S32 end, LLTextBase& editor );
+ LLNormalTextSegment( LLStyleConstSP style, S32 start, S32 end, LLTextBase& editor );
LLNormalTextSegment( const LLColor4& color, S32 start, S32 end, LLTextBase& editor, BOOL is_visible = TRUE);
~LLNormalTextSegment();
@@ -436,9 +443,8 @@ public:
/*virtual*/ F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRect& draw_rect);
/*virtual*/ bool canEdit() const { return true; }
/*virtual*/ const LLColor4& getColor() const { return mStyle->getColor(); }
- /*virtual*/ void setColor(const LLColor4 &color) { mStyle->setColor(color); }
- /*virtual*/ const LLStyleSP getStyle() const { return mStyle; }
- /*virtual*/ void setStyle(const LLStyleSP &style) { mStyle = style; }
+ /*virtual*/ LLStyleConstSP getStyle() const { return mStyle; }
+ /*virtual*/ void setStyle(LLStyleConstSP style) { mStyle = style; }
/*virtual*/ void setToken( LLKeywordToken* token ) { mToken = token; }
/*virtual*/ LLKeywordToken* getToken() const { return mToken; }
/*virtual*/ BOOL getToolTip( std::string& msg ) const;
@@ -456,7 +462,7 @@ protected:
protected:
class LLTextBase& mEditor;
- LLStyleSP mStyle;
+ LLStyleConstSP mStyle;
S32 mFontHeight;
LLKeywordToken* mToken;
std::string mTooltip;
diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp
index f2c3879a6c..4fd62045e8 100644
--- a/indra/llui/lltexteditor.cpp
+++ b/indra/llui/lltexteditor.cpp
@@ -237,13 +237,16 @@ private:
///////////////////////////////////////////////////////////////////
LLTextEditor::Params::Params()
: default_text("default_text"),
+ prevalidate_callback("prevalidate_callback"),
embedded_items("embedded_items", false),
ignore_tab("ignore_tab", true),
- handle_edit_keys_directly("handle_edit_keys_directly", false),
show_line_numbers("show_line_numbers", false),
default_color("default_color"),
- commit_on_focus_lost("commit_on_focus_lost", false)
-{}
+ commit_on_focus_lost("commit_on_focus_lost", false),
+ show_context_menu("show_context_menu")
+{
+ addSynonym(prevalidate_callback, "text_type");
+}
LLTextEditor::LLTextEditor(const LLTextEditor::Params& p) :
LLTextBase(p),
@@ -254,11 +257,12 @@ LLTextEditor::LLTextEditor(const LLTextEditor::Params& p) :
mShowLineNumbers ( p.show_line_numbers ),
mCommitOnFocusLost( p.commit_on_focus_lost),
mAllowEmbeddedItems( p.embedded_items ),
- mHandleEditKeysDirectly( p.handle_edit_keys_directly ),
mMouseDownX(0),
mMouseDownY(0),
mTabsToNextField(p.ignore_tab),
- mContextMenu(NULL)
+ mPrevalidateFunc(p.prevalidate_callback()),
+ mContextMenu(NULL),
+ mShowContextMenu(p.show_context_menu)
{
mDefaultFont = p.font;
@@ -299,12 +303,6 @@ LLTextEditor::~LLTextEditor()
{
gFocusMgr.releaseFocusIfNeeded( this ); // calls onCommit() while LLTextEditor still valid
- // Route menu back to the default
- if( gEditMenuHandler == this )
- {
- gEditMenuHandler = NULL;
- }
-
// Scrollbar is deleted by LLView
std::for_each(mUndoStack.begin(), mUndoStack.end(), DeletePointer());
@@ -318,6 +316,17 @@ LLTextEditor::~LLTextEditor()
void LLTextEditor::setText(const LLStringExplicit &utf8str, const LLStyle::Params& input_params)
{
+ // validate incoming text if necessary
+ if (mPrevalidateFunc)
+ {
+ LLWString test_text = utf8str_to_wstring(utf8str);
+ if (!mPrevalidateFunc(test_text))
+ {
+ // not valid text, nothing to do
+ return;
+ }
+ }
+
blockUndo();
deselect();
@@ -490,21 +499,6 @@ void LLTextEditor::getSegmentsInRange(LLTextEditor::segment_vec_t& segments_out,
}
}
-// virtual
-BOOL LLTextEditor::canDeselect() const
-{
- return hasSelection();
-}
-
-
-void LLTextEditor::deselect()
-{
- mSelectionStart = 0;
- mSelectionEnd = 0;
- mIsSelecting = FALSE;
-}
-
-
BOOL LLTextEditor::selectionContainsLineBreaks()
{
if (hasSelection())
@@ -651,6 +645,7 @@ void LLTextEditor::selectAll()
mSelectionStart = getLength();
mSelectionEnd = 0;
setCursorPos(mSelectionEnd);
+ updatePrimary();
}
BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask)
@@ -718,9 +713,13 @@ BOOL LLTextEditor::handleRightMouseDown(S32 x, S32 y, MASK mask)
{
setFocus(TRUE);
}
- if (!LLTextBase::handleRightMouseDown(x, y, mask))
+ // Prefer editor menu if it has selection. See EXT-6806.
+ if (hasSelection() || !LLTextBase::handleRightMouseDown(x, y, mask))
{
- showContextMenu(x, y);
+ if(getShowContextMenu())
+ {
+ showContextMenu(x, y);
+ }
}
return TRUE;
}
@@ -906,6 +905,21 @@ S32 LLTextEditor::execute( TextCmd* cmd )
// Push the new command is now on the top (front) of the undo stack.
mUndoStack.push_front(cmd);
mLastCmd = cmd;
+
+ bool need_to_rollback = mPrevalidateFunc
+ && !mPrevalidateFunc(getViewModel()->getDisplay());
+ if (need_to_rollback)
+ {
+ // get rid of this last command and clean up undo stack
+ undo();
+
+ // remove any evidence of this command from redo history
+ mUndoStack.pop_front();
+ delete cmd;
+
+ // failure, nothing changed
+ delta = 0;
+ }
}
else
{
@@ -990,7 +1004,7 @@ void LLTextEditor::removeCharOrTab()
}
else
{
- reportBadKeystroke();
+ LLUI::reportBadKeystroke();
}
}
@@ -1013,7 +1027,7 @@ void LLTextEditor::removeChar()
}
else
{
- reportBadKeystroke();
+ LLUI::reportBadKeystroke();
}
}
@@ -1029,7 +1043,21 @@ S32 LLTextEditor::addChar(S32 pos, llwchar wc)
if (mLastCmd && mLastCmd->canExtend(pos))
{
S32 delta = 0;
+ if (mPrevalidateFunc)
+ {
+ // get a copy of current text contents
+ LLWString test_string(getViewModel()->getDisplay());
+
+ // modify text contents as if this addChar succeeded
+ llassert(pos <= (S32)test_string.size());
+ test_string.insert(pos, 1, wc);
+ if (!mPrevalidateFunc( test_string))
+ {
+ return 0;
+ }
+ }
mLastCmd->extendAndExecute(this, pos, wc, &delta);
+
return delta;
}
else
@@ -1149,22 +1177,6 @@ BOOL LLTextEditor::handleSelectionKey(const KEY key, const MASK mask)
}
}
- if( !handled && mHandleEditKeysDirectly )
- {
- if( (MASK_CONTROL & mask) && ('A' == key) )
- {
- if( canSelectAll() )
- {
- selectAll();
- }
- else
- {
- reportBadKeystroke();
- }
- handled = TRUE;
- }
- }
-
if( handled )
{
// take selection to 'primary' clipboard
@@ -1198,6 +1210,7 @@ BOOL LLTextEditor::handleNavigationKey(const KEY key, const MASK mask)
case KEY_DOWN:
changeLine( 1 );
+ deselect();
break;
case KEY_PAGE_DOWN:
@@ -1211,7 +1224,7 @@ BOOL LLTextEditor::handleNavigationKey(const KEY key, const MASK mask)
case KEY_LEFT:
if( hasSelection() )
{
- setCursorPos(llmin( mCursorPos - 1, mSelectionStart, mSelectionEnd ));
+ setCursorPos(llmin( mSelectionStart, mSelectionEnd ));
}
else
{
@@ -1221,7 +1234,7 @@ BOOL LLTextEditor::handleNavigationKey(const KEY key, const MASK mask)
}
else
{
- reportBadKeystroke();
+ LLUI::reportBadKeystroke();
}
}
break;
@@ -1229,7 +1242,7 @@ BOOL LLTextEditor::handleNavigationKey(const KEY key, const MASK mask)
case KEY_RIGHT:
if( hasSelection() )
{
- setCursorPos(llmax( mCursorPos + 1, mSelectionStart, mSelectionEnd ));
+ setCursorPos(llmax( mSelectionStart, mSelectionEnd ));
}
else
{
@@ -1239,7 +1252,7 @@ BOOL LLTextEditor::handleNavigationKey(const KEY key, const MASK mask)
}
else
{
- reportBadKeystroke();
+ LLUI::reportBadKeystroke();
}
}
break;
@@ -1250,6 +1263,11 @@ BOOL LLTextEditor::handleNavigationKey(const KEY key, const MASK mask)
}
}
+ if (handled)
+ {
+ deselect();
+ }
+
return handled;
}
@@ -1285,8 +1303,6 @@ void LLTextEditor::cut()
gClipboard.copyFromSubstring( getWText(), left_pos, length, mSourceID );
deleteSelection( FALSE );
- needsReflow();
-
onKeyStroke();
}
@@ -1391,8 +1407,6 @@ void LLTextEditor::pasteHelper(bool is_primary)
setCursorPos(mCursorPos + insert(mCursorPos, clean_string, FALSE, LLTextSegmentPtr()));
deselect();
- needsReflow();
-
onKeyStroke();
}
@@ -1506,75 +1520,13 @@ BOOL LLTextEditor::handleControlKey(const KEY key, const MASK mask)
return handled;
}
-BOOL LLTextEditor::handleEditKey(const KEY key, const MASK mask)
-{
- BOOL handled = FALSE;
- // Standard edit keys (Ctrl-X, Delete, etc,) are handled here instead of routed by the menu system.
- if( KEY_DELETE == key )
+BOOL LLTextEditor::handleSpecialKey(const KEY key, const MASK mask)
{
- if( canDoDelete() )
- {
- doDelete();
- }
- else
- {
- reportBadKeystroke();
- }
- handled = TRUE;
- }
- else
- if( MASK_CONTROL & mask )
- {
- if( 'C' == key )
- {
- if( canCopy() )
- {
- copy();
- }
- else
- {
- reportBadKeystroke();
- }
- handled = TRUE;
- }
- else
- if( 'V' == key )
- {
- if( canPaste() )
- {
- paste();
- }
- else
- {
- reportBadKeystroke();
- }
- handled = TRUE;
- }
- else
- if( 'X' == key )
- {
- if( canCut() )
- {
- cut();
- }
- else
- {
- reportBadKeystroke();
- }
- handled = TRUE;
- }
- }
-
- return handled;
-}
-
-
-BOOL LLTextEditor::handleSpecialKey(const KEY key, const MASK mask, BOOL* return_key_hit)
-{
- *return_key_hit = FALSE;
BOOL handled = TRUE;
+ if (mReadOnly) return FALSE;
+
switch( key )
{
case KEY_INSERT:
@@ -1596,7 +1548,7 @@ BOOL LLTextEditor::handleSpecialKey(const KEY key, const MASK mask, BOOL* return
}
else
{
- reportBadKeystroke();
+ LLUI::reportBadKeystroke();
}
break;
@@ -1649,6 +1601,10 @@ BOOL LLTextEditor::handleSpecialKey(const KEY key, const MASK mask, BOOL* return
break;
}
+ if (handled)
+ {
+ onKeyStroke();
+ }
return handled;
}
@@ -1669,9 +1625,6 @@ void LLTextEditor::unindentLineBeforeCloseBrace()
BOOL LLTextEditor::handleKeyHere(KEY key, MASK mask )
{
BOOL handled = FALSE;
- BOOL selection_modified = FALSE;
- BOOL return_key_hit = FALSE;
- BOOL text_may_have_changed = TRUE;
// Special case for TAB. If want to move to next field, report
// not handled and let the parent take care of field movement.
@@ -1679,118 +1632,24 @@ BOOL LLTextEditor::handleKeyHere(KEY key, MASK mask )
{
return FALSE;
}
- /*
- if (KEY_F10 == key)
- {
- LLComboBox::Params cp;
- cp.name = "combo box";
- cp.label = "my combo";
- cp.rect.width = 100;
- cp.rect.height = 20;
- cp.items.add().label = "item 1";
- cp.items.add().label = "item 2";
- cp.items.add().label = "item 3";
- appendWidget(LLUICtrlFactory::create<LLComboBox>(cp), "combo", true, false);
- }
- if (KEY_F11 == key)
+ if (mReadOnly && mScroller)
{
- LLButton::Params bp;
- bp.name = "text button";
- bp.label = "Click me";
- bp.rect.width = 100;
- bp.rect.height = 20;
-
- appendWidget(LLUICtrlFactory::create<LLButton>(bp), "button", true, false);
- }
- */
- if (mReadOnly)
- {
- if(mScroller)
- {
- handled = mScroller->handleKeyHere( key, mask );
+ handled = (mScroller && mScroller->handleKeyHere( key, mask ))
+ || handleSelectionKey(key, mask)
+ || handleControlKey(key, mask);
}
else
{
- handled = handleNavigationKey( key, mask );
- }
-
- }
- else
- {
- // handle navigation keys ourself
- handled = handleNavigationKey( key, mask );
- }
-
-
- if( handled )
- {
- text_may_have_changed = FALSE;
- }
-
- if( !handled )
- {
- handled = handleSelectionKey( key, mask );
- if( handled )
- {
- selection_modified = TRUE;
- }
- }
-
- if( !handled )
- {
- handled = handleControlKey( key, mask );
- if( handled )
- {
- selection_modified = TRUE;
- }
- }
-
- if( !handled && mHandleEditKeysDirectly )
- {
- handled = handleEditKey( key, mask );
- if( handled )
- {
- selection_modified = TRUE;
- text_may_have_changed = TRUE;
- }
- }
-
- // Handle most keys only if the text editor is writeable.
- if( !mReadOnly )
- {
- if( !handled )
- {
- handled = handleSpecialKey( key, mask, &return_key_hit );
- if( handled )
- {
- selection_modified = TRUE;
- text_may_have_changed = TRUE;
- }
- }
-
+ handled = handleNavigationKey( key, mask )
+ || handleSelectionKey(key, mask)
+ || handleControlKey(key, mask)
+ || handleSpecialKey(key, mask);
}
if( handled )
{
resetCursorBlink();
-
- // Most keystrokes will make the selection box go away, but not all will.
- if( !selection_modified &&
- KEY_SHIFT != key &&
- KEY_CONTROL != key &&
- KEY_ALT != key &&
- KEY_CAPSLOCK )
- {
- deselect();
- }
-
- if(text_may_have_changed)
- {
- needsReflow();
-
- onKeyStroke();
- }
needsScroll();
}
@@ -1831,8 +1690,6 @@ BOOL LLTextEditor::handleUnicodeCharHere(llwchar uni_char)
// Most keystrokes will make the selection box go away, but not all will.
deselect();
- needsReflow();
-
onKeyStroke();
}
@@ -1891,8 +1748,6 @@ void LLTextEditor::doDelete()
}
onKeyStroke();
-
- needsReflow();
}
//----------------------------------------------------------------------------
@@ -1935,8 +1790,6 @@ void LLTextEditor::undo()
setCursorPos(pos);
- needsReflow();
-
onKeyStroke();
}
@@ -1979,8 +1832,6 @@ void LLTextEditor::redo()
setCursorPos(pos);
- needsReflow();
-
onKeyStroke();
}
@@ -2040,6 +1891,20 @@ void LLTextEditor::showContextMenu(S32 x, S32 y)
LLMenuHolderGL::child_registry_t::instance());
}
+ // Route menu to this class
+ // previously this was done in ::handleRightMoseDown:
+ //if(hasTabStop())
+ // setFocus(TRUE) - why? weird...
+ // and then inside setFocus
+ // ....
+ // gEditMenuHandler = this;
+ // ....
+ // but this didn't work in all cases and just weird...
+ //why not here?
+ // (all this was done for EXT-4443)
+
+ gEditMenuHandler = this;
+
S32 screen_x, screen_y;
localPointToScreen(x, y, &screen_x, &screen_y);
mContextMenu->show(screen_x, screen_y);
@@ -2285,7 +2150,7 @@ void LLTextEditor::getCurrentLineAndColumn( S32* line, S32* col, BOOL include_wo
void LLTextEditor::autoIndent()
{
// Count the number of spaces in the current line
- S32 line = getLineNumFromDocIndex(mCursorPos);
+ S32 line = getLineNumFromDocIndex(mCursorPos, false);
S32 line_start = getLineStart(line);
S32 space_count = 0;
S32 i;
@@ -2325,8 +2190,6 @@ void LLTextEditor::insertText(const std::string &new_text)
setCursorPos(mCursorPos + insert( mCursorPos, utf8str_to_wstring(new_text), FALSE, LLTextSegmentPtr() ));
- needsReflow();
-
setEnabled( enabled );
}
@@ -2349,8 +2212,6 @@ void LLTextEditor::appendWidget(const LLInlineViewSegment::Params& params, const
LLTextSegmentPtr segment = new LLInlineViewSegment(params, old_length, old_length + widget_wide_text.size());
insert(getLength(), widget_wide_text, FALSE, segment);
- needsReflow();
-
// Set the cursor and scroll position
if( selection_start != selection_end )
{
@@ -2375,52 +2236,6 @@ void LLTextEditor::appendWidget(const LLInlineViewSegment::Params& params, const
}
}
-
-void LLTextEditor::replaceUrlLabel(const std::string &url,
- const std::string &label)
-{
- // get the full (wide) text for the editor so we can change it
- LLWString text = getWText();
- LLWString wlabel = utf8str_to_wstring(label);
- bool modified = false;
- S32 seg_start = 0;
-
- // iterate through each segment looking for ones styled as links
- segment_set_t::iterator it;
- for (it = mSegments.begin(); it != mSegments.end(); ++it)
- {
- LLTextSegment *seg = *it;
- const LLStyleSP style = seg->getStyle();
-
- // update segment start/end length in case we replaced text earlier
- S32 seg_length = seg->getEnd() - seg->getStart();
- seg->setStart(seg_start);
- seg->setEnd(seg_start + seg_length);
-
- // if we find a link with our Url, then replace the label
- if (style->isLink() && style->getLinkHREF() == url)
- {
- S32 start = seg->getStart();
- S32 end = seg->getEnd();
- text = text.substr(0, start) + wlabel + text.substr(end, text.size() - end + 1);
- seg->setEnd(start + wlabel.size());
- modified = true;
- }
-
- // work out the character offset for the next segment
- seg_start = seg->getEnd();
- }
-
- // update the editor with the new (wide) text string
- if (modified)
- {
- getViewModel()->setDisplay(text);
- deselect();
- setCursorPos(mCursorPos);
- needsReflow();
- }
-}
-
void LLTextEditor::removeTextFromEnd(S32 num_chars)
{
if (num_chars <= 0) return;
@@ -2432,7 +2247,6 @@ void LLTextEditor::removeTextFromEnd(S32 num_chars)
mSelectionStart = llclamp(mSelectionStart, 0, len);
mSelectionEnd = llclamp(mSelectionEnd, 0, len);
- needsReflow();
needsScroll();
}
@@ -2491,8 +2305,6 @@ BOOL LLTextEditor::tryToRevertToPristineState()
i--;
}
}
-
- needsReflow();
}
return isPristine(); // TRUE => success
@@ -2528,9 +2340,9 @@ void LLTextEditor::loadKeywords(const std::string& filename,
void LLTextEditor::updateSegments()
{
- LLFastTimer ft(FTM_SYNTAX_HIGHLIGHTING);
- if (mKeywords.isLoaded())
+ if (mReflowIndex < S32_MAX && mKeywords.isLoaded())
{
+ LLFastTimer ft(FTM_SYNTAX_HIGHLIGHTING);
// HACK: No non-ascii keywords for now
segment_vec_t segment_list;
mKeywords.findSegments(&segment_list, getWText(), mDefaultColor.get(), *this);
@@ -2559,13 +2371,16 @@ void LLTextEditor::updateLinkSegments()
// if the link's label (what the user can edit) is a valid Url,
// then update the link's HREF to be the same as the label text.
// This lets users edit Urls in-place.
- LLStyleSP style = static_cast<LLStyleSP>(segment->getStyle());
+ LLStyleConstSP style = segment->getStyle();
+ LLStyleSP new_style(new LLStyle(*style));
LLWString url_label = wtext.substr(segment->getStart(), segment->getEnd()-segment->getStart());
if (LLUrlRegistry::instance().hasUrl(url_label))
{
std::string new_url = wstring_to_utf8str(url_label);
LLStringUtil::trim(new_url);
- style->setLinkHREF(new_url);
+ new_style->setLinkHREF(new_url);
+ LLStyleConstSP sp(new_style);
+ segment->setStyle(sp);
}
}
}
@@ -2664,7 +2479,6 @@ BOOL LLTextEditor::importBuffer(const char* buffer, S32 length )
startOfDoc();
deselect();
- needsReflow();
return success;
}
@@ -2768,7 +2582,6 @@ void LLTextEditor::updatePreedit(const LLWString &preedit_string,
mPreeditStandouts = preedit_standouts;
- needsReflow();
setCursorPos(insert_preedit_at + caret_position);
// Update of the preedit should be caused by some key strokes.
diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h
index 043dda8fa6..9b3ab9414c 100644
--- a/indra/llui/lltexteditor.h
+++ b/indra/llui/lltexteditor.h
@@ -44,6 +44,7 @@
#include "lldarray.h"
#include "llviewborder.h" // for params
#include "lltextbase.h"
+#include "lltextvalidate.h"
#include "llpreeditor.h"
#include "llcontrol.h"
@@ -63,12 +64,13 @@ public:
struct Params : public LLInitParam::Block<Params, LLTextBase::Params>
{
Optional<std::string> default_text;
+ Optional<LLTextValidate::validate_func_t, LLTextValidate::ValidateTextNamedFuncs> prevalidate_callback;
Optional<bool> embedded_items,
ignore_tab,
- handle_edit_keys_directly,
show_line_numbers,
- commit_on_focus_lost;
+ commit_on_focus_lost,
+ show_context_menu;
//colors
Optional<LLUIColor> default_color;
@@ -143,13 +145,10 @@ public:
virtual BOOL canDoDelete() const;
virtual void selectAll();
virtual BOOL canSelectAll() const;
- virtual void deselect();
- virtual BOOL canDeselect() const;
void selectNext(const std::string& search_text_in, BOOL case_insensitive, BOOL wrap = TRUE);
BOOL replaceText(const std::string& search_text, const std::string& replace_text, BOOL case_insensitive, BOOL wrap = TRUE);
void replaceTextAll(const std::string& search_text, const std::string& replace_text, BOOL case_insensitive);
- void replaceUrlLabel(const std::string &url, const std::string &label);
// Undo/redo stack
void blockUndo();
@@ -201,6 +200,9 @@ public:
const LLTextSegmentPtr getPreviousSegment() const;
void getSelectedSegments(segment_vec_t& segments) const;
+ void setShowContextMenu(bool show) { mShowContextMenu = show; }
+ bool getShowContextMenu() const { return mShowContextMenu; }
+
protected:
void showContextMenu(S32 x, S32 y);
void drawPreeditMarker();
@@ -213,13 +215,10 @@ protected:
S32 indentLine( S32 pos, S32 spaces );
void unindentLineBeforeCloseBrace();
- void reportBadKeystroke() { make_ui_sound("UISndBadKeystroke"); }
-
BOOL handleNavigationKey(const KEY key, const MASK mask);
- BOOL handleSpecialKey(const KEY key, const MASK mask, BOOL* return_key_hit);
+ BOOL handleSpecialKey(const KEY key, const MASK mask);
BOOL handleSelectionKey(const KEY key, const MASK mask);
BOOL handleControlKey(const KEY key, const MASK mask);
- BOOL handleEditKey(const KEY key, const MASK mask);
BOOL selectionContainsLineBreaks();
void deleteSelection(BOOL transient_operation);
@@ -320,16 +319,14 @@ private:
BOOL mTakesFocus;
BOOL mAllowEmbeddedItems;
+ bool mShowContextMenu;
LLUUID mSourceID;
- // If true, the standard edit keys (Ctrl-X, Delete, etc,) are handled here
- //instead of routed by the menu system
- BOOL mHandleEditKeysDirectly;
-
LLCoordGL mLastIMEPosition; // Last position of the IME editor
keystroke_signal_t mKeystrokeSignal;
+ LLTextValidate::validate_func_t mPrevalidateFunc;
LLContextMenu* mContextMenu;
}; // end class LLTextEditor
diff --git a/indra/llui/lltextvalidate.cpp b/indra/llui/lltextvalidate.cpp
new file mode 100644
index 0000000000..8b6bc5bd7d
--- /dev/null
+++ b/indra/llui/lltextvalidate.cpp
@@ -0,0 +1,302 @@
+/**
+ * @file lltextvalidate.cpp
+ * @brief Text validation helper functions
+ *
+ * $LicenseInfo:firstyear=2001&license=viewergpl$
+ *
+ * Copyright (c) 2001-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$
+ */
+
+// Text editor widget to let users enter a single line.
+
+#include "linden_common.h"
+
+#include "lltextvalidate.h"
+#include "llresmgr.h" // for LLLocale
+
+namespace LLTextValidate
+{
+ void ValidateTextNamedFuncs::declareValues()
+ {
+ declare("ascii", validateASCII);
+ declare("float", validateFloat);
+ declare("int", validateInt);
+ declare("positive_s32", validatePositiveS32);
+ declare("non_negative_s32", validateNonNegativeS32);
+ declare("alpha_num", validateAlphaNum);
+ declare("alpha_num_space", validateAlphaNumSpace);
+ declare("ascii_printable_no_pipe", validateASCIIPrintableNoPipe);
+ declare("ascii_printable_no_space", validateASCIIPrintableNoSpace);
+ }
+
+ // Limits what characters can be used to [1234567890.-] with [-] only valid in the first position.
+ // Does NOT ensure that the string is a well-formed number--that's the job of post-validation--for
+ // the simple reasons that intermediate states may be invalid even if the final result is valid.
+ //
+ bool validateFloat(const LLWString &str)
+ {
+ LLLocale locale(LLLocale::USER_LOCALE);
+
+ bool success = TRUE;
+ LLWString trimmed = str;
+ LLWStringUtil::trim(trimmed);
+ S32 len = trimmed.length();
+ if( 0 < len )
+ {
+ // May be a comma or period, depending on the locale
+ llwchar decimal_point = (llwchar)LLResMgr::getInstance()->getDecimalPoint();
+
+ S32 i = 0;
+
+ // First character can be a negative sign
+ if( '-' == trimmed[0] )
+ {
+ i++;
+ }
+
+ for( ; i < len; i++ )
+ {
+ if( (decimal_point != trimmed[i] ) && !LLStringOps::isDigit( trimmed[i] ) )
+ {
+ success = FALSE;
+ break;
+ }
+ }
+ }
+
+ return success;
+ }
+
+ // Limits what characters can be used to [1234567890-] with [-] only valid in the first position.
+ // Does NOT ensure that the string is a well-formed number--that's the job of post-validation--for
+ // the simple reasons that intermediate states may be invalid even if the final result is valid.
+ //
+ bool validateInt(const LLWString &str)
+ {
+ LLLocale locale(LLLocale::USER_LOCALE);
+
+ bool success = TRUE;
+ LLWString trimmed = str;
+ LLWStringUtil::trim(trimmed);
+ S32 len = trimmed.length();
+ if( 0 < len )
+ {
+ S32 i = 0;
+
+ // First character can be a negative sign
+ if( '-' == trimmed[0] )
+ {
+ i++;
+ }
+
+ for( ; i < len; i++ )
+ {
+ if( !LLStringOps::isDigit( trimmed[i] ) )
+ {
+ success = FALSE;
+ break;
+ }
+ }
+ }
+
+ return success;
+ }
+
+ bool validatePositiveS32(const LLWString &str)
+ {
+ LLLocale locale(LLLocale::USER_LOCALE);
+
+ LLWString trimmed = str;
+ LLWStringUtil::trim(trimmed);
+ S32 len = trimmed.length();
+ bool success = TRUE;
+ if(0 < len)
+ {
+ if(('-' == trimmed[0]) || ('0' == trimmed[0]))
+ {
+ success = FALSE;
+ }
+ S32 i = 0;
+ while(success && (i < len))
+ {
+ if(!LLStringOps::isDigit(trimmed[i++]))
+ {
+ success = FALSE;
+ }
+ }
+ }
+ if (success)
+ {
+ S32 val = strtol(wstring_to_utf8str(trimmed).c_str(), NULL, 10);
+ if (val <= 0)
+ {
+ success = FALSE;
+ }
+ }
+ return success;
+ }
+
+ bool validateNonNegativeS32(const LLWString &str)
+ {
+ LLLocale locale(LLLocale::USER_LOCALE);
+
+ LLWString trimmed = str;
+ LLWStringUtil::trim(trimmed);
+ S32 len = trimmed.length();
+ bool success = TRUE;
+ if(0 < len)
+ {
+ if('-' == trimmed[0])
+ {
+ success = FALSE;
+ }
+ S32 i = 0;
+ while(success && (i < len))
+ {
+ if(!LLStringOps::isDigit(trimmed[i++]))
+ {
+ success = FALSE;
+ }
+ }
+ }
+ if (success)
+ {
+ S32 val = strtol(wstring_to_utf8str(trimmed).c_str(), NULL, 10);
+ if (val < 0)
+ {
+ success = FALSE;
+ }
+ }
+ return success;
+ }
+
+ bool validateAlphaNum(const LLWString &str)
+ {
+ LLLocale locale(LLLocale::USER_LOCALE);
+
+ bool rv = TRUE;
+ S32 len = str.length();
+ if(len == 0) return rv;
+ while(len--)
+ {
+ if( !LLStringOps::isAlnum((char)str[len]) )
+ {
+ rv = FALSE;
+ break;
+ }
+ }
+ return rv;
+ }
+
+ bool validateAlphaNumSpace(const LLWString &str)
+ {
+ LLLocale locale(LLLocale::USER_LOCALE);
+
+ bool rv = TRUE;
+ S32 len = str.length();
+ if(len == 0) return rv;
+ while(len--)
+ {
+ if(!(LLStringOps::isAlnum((char)str[len]) || (' ' == str[len])))
+ {
+ rv = FALSE;
+ break;
+ }
+ }
+ return rv;
+ }
+
+ // Used for most names of things stored on the server, due to old file-formats
+ // that used the pipe (|) for multiline text storage. Examples include
+ // inventory item names, parcel names, object names, etc.
+ bool validateASCIIPrintableNoPipe(const LLWString &str)
+ {
+ bool rv = TRUE;
+ S32 len = str.length();
+ if(len == 0) return rv;
+ while(len--)
+ {
+ llwchar wc = str[len];
+ if (wc < 0x20
+ || wc > 0x7f
+ || wc == '|')
+ {
+ rv = FALSE;
+ break;
+ }
+ if(!(wc == ' '
+ || LLStringOps::isAlnum((char)wc)
+ || LLStringOps::isPunct((char)wc) ) )
+ {
+ rv = FALSE;
+ break;
+ }
+ }
+ return rv;
+ }
+
+
+ // Used for avatar names
+ bool validateASCIIPrintableNoSpace(const LLWString &str)
+ {
+ bool rv = TRUE;
+ S32 len = str.length();
+ if(len == 0) return rv;
+ while(len--)
+ {
+ llwchar wc = str[len];
+ if (wc < 0x20
+ || wc > 0x7f
+ || LLStringOps::isSpace(wc))
+ {
+ rv = FALSE;
+ break;
+ }
+ if( !(LLStringOps::isAlnum((char)str[len]) ||
+ LLStringOps::isPunct((char)str[len]) ) )
+ {
+ rv = FALSE;
+ break;
+ }
+ }
+ return rv;
+ }
+
+ bool validateASCII(const LLWString &str)
+ {
+ bool rv = TRUE;
+ S32 len = str.length();
+ while(len--)
+ {
+ if (str[len] < 0x20 || str[len] > 0x7f)
+ {
+ rv = FALSE;
+ break;
+ }
+ }
+ return rv;
+ }
+}
diff --git a/indra/llui/lltextvalidate.h b/indra/llui/lltextvalidate.h
new file mode 100644
index 0000000000..ffb4e85e7c
--- /dev/null
+++ b/indra/llui/lltextvalidate.h
@@ -0,0 +1,63 @@
+/**
+ * @file lltextbase.h
+ * @author Martin Reddy
+ * @brief The base class of text box/editor, providing Url handling support
+ *
+ * $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_LLTEXTVALIDATE_H
+#define LL_LLTEXTVALIDATE_H
+
+#include "llstring.h"
+#include "llinitparam.h"
+#include <boost/function.hpp>
+
+namespace LLTextValidate
+{
+ typedef boost::function<BOOL (const LLWString &wstr)> validate_func_t;
+
+ struct ValidateTextNamedFuncs
+ : public LLInitParam::TypeValuesHelper<validate_func_t, ValidateTextNamedFuncs>
+ {
+ static void declareValues();
+ };
+
+ bool validateFloat(const LLWString &str );
+ bool validateInt(const LLWString &str );
+ bool validatePositiveS32(const LLWString &str);
+ bool validateNonNegativeS32(const LLWString &str);
+ bool validateAlphaNum(const LLWString &str );
+ bool validateAlphaNumSpace(const LLWString &str );
+ bool validateASCIIPrintableNoPipe(const LLWString &str);
+ bool validateASCIIPrintableNoSpace(const LLWString &str);
+ bool validateASCII(const LLWString &str);
+}
+
+
+#endif
diff --git a/indra/llui/lltooltip.cpp b/indra/llui/lltooltip.cpp
index 01c7a81309..ed7fd02e14 100644
--- a/indra/llui/lltooltip.cpp
+++ b/indra/llui/lltooltip.cpp
@@ -129,12 +129,6 @@ BOOL LLToolTipView::handleScrollWheel( S32 x, S32 y, S32 clicks )
return FALSE;
}
-void LLToolTipView::onMouseLeave(S32 x, S32 y, MASK mask)
-{
- LLToolTipMgr::instance().blockToolTips();
-}
-
-
void LLToolTipView::drawStickyRect()
{
gl_rect_2d(LLToolTipMgr::instance().getMouseNearRect(), LLColor4::white, false);
@@ -400,7 +394,8 @@ bool LLToolTip::hasClickCallback()
//
LLToolTipMgr::LLToolTipMgr()
-: mToolTip(NULL),
+: mToolTipsBlocked(false),
+ mToolTip(NULL),
mNeedsToolTip(false)
{}
diff --git a/indra/llui/lltooltip.h b/indra/llui/lltooltip.h
index 7978b6a583..24e32b9b24 100644
--- a/indra/llui/lltooltip.h
+++ b/indra/llui/lltooltip.h
@@ -56,8 +56,6 @@ public:
/*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask);
/*virtual*/ BOOL handleScrollWheel( S32 x, S32 y, S32 clicks );
- /*virtual*/ void onMouseLeave(S32 x, S32 y, MASK mask);
-
void drawStickyRect();
/*virtual*/ void draw();
@@ -129,7 +127,8 @@ private:
class LLInspector : public LLToolTip
{
public:
- struct Params : public LLInitParam::Block<Params, LLToolTip::Params> {};
+ struct Params : public LLInitParam::Block<Params, LLToolTip::Params>
+ {};
};
class LLToolTipMgr : public LLSingleton<LLToolTipMgr>
diff --git a/indra/llui/llui.cpp b/indra/llui/llui.cpp
index d0ed3b6fca..bf12384a28 100644
--- a/indra/llui/llui.cpp
+++ b/indra/llui/llui.cpp
@@ -39,6 +39,7 @@
// Linden library includes
#include "v2math.h"
+#include "m3math.h"
#include "v4color.h"
#include "llrender.h"
#include "llrect.h"
@@ -55,6 +56,7 @@
#include "llfloaterreg.h"
#include "llmenugl.h"
#include "llmenubutton.h"
+#include "llloadingindicator.h"
#include "llwindow.h"
// for registration
@@ -85,12 +87,18 @@ std::list<std::string> gUntranslated;
/*static*/ LLHelp* LLUI::sHelpImpl = NULL;
/*static*/ std::vector<std::string> LLUI::sXUIPaths;
/*static*/ LLFrameTimer LLUI::sMouseIdleTimer;
+/*static*/ LLUI::add_popup_t LLUI::sAddPopupFunc;
+/*static*/ LLUI::remove_popup_t LLUI::sRemovePopupFunc;
+/*static*/ LLUI::clear_popups_t LLUI::sClearPopupsFunc;
// register filtereditor here
static LLDefaultChildRegistry::Register<LLFilterEditor> register_filter_editor("filter_editor");
static LLDefaultChildRegistry::Register<LLFlyoutButton> register_flyout_button("flyout_button");
static LLDefaultChildRegistry::Register<LLSearchEditor> register_search_editor("search_editor");
+
+// register other widgets which otherwise may not be linked in
static LLDefaultChildRegistry::Register<LLMenuButton> register_menu_button("menu_button");
+static LLDefaultChildRegistry::Register<LLLoadingIndicator> register_loading_indicator("loading_indicator");
//
@@ -180,19 +188,19 @@ void gl_rect_2d_offset_local( S32 left, S32 top, S32 right, S32 bottom, const LL
void gl_rect_2d_offset_local( S32 left, S32 top, S32 right, S32 bottom, S32 pixel_offset, BOOL filled)
{
- gGL.pushMatrix();
+ gGL.pushUIMatrix();
left += LLFontGL::sCurOrigin.mX;
right += LLFontGL::sCurOrigin.mX;
bottom += LLFontGL::sCurOrigin.mY;
top += LLFontGL::sCurOrigin.mY;
- glLoadIdentity();
+ gGL.loadUIIdentity();
gl_rect_2d(llfloor((F32)left * LLUI::sGLScaleFactor.mV[VX]) - pixel_offset,
llfloor((F32)top * LLUI::sGLScaleFactor.mV[VY]) + pixel_offset,
llfloor((F32)right * LLUI::sGLScaleFactor.mV[VX]) + pixel_offset,
llfloor((F32)bottom * LLUI::sGLScaleFactor.mV[VY]) - pixel_offset,
filled);
- gGL.popMatrix();
+ gGL.popUIMatrix();
}
@@ -203,7 +211,7 @@ void gl_rect_2d(S32 left, S32 top, S32 right, S32 bottom, BOOL filled )
// Counterclockwise quad will face the viewer
if( filled )
- {
+ {
gGL.begin( LLRender::QUADS );
gGL.vertex2i(left, top);
gGL.vertex2i(left, bottom);
@@ -508,9 +516,9 @@ void gl_draw_scaled_image_with_border(S32 x, S32 y, S32 width, S32 height, LLTex
gGL.getTexUnit(0)->setTextureAlphaBlend(LLTexUnit::TBO_MULT, LLTexUnit::TBS_TEX_ALPHA, LLTexUnit::TBS_VERT_ALPHA);
}
- gGL.pushMatrix();
+ gGL.pushUIMatrix();
{
- gGL.translatef((F32)x, (F32)y, 0.f);
+ gGL.translateUI((F32)x, (F32)y, 0.f);
gGL.getTexUnit(0)->bind(image);
@@ -637,7 +645,7 @@ void gl_draw_scaled_image_with_border(S32 x, S32 y, S32 width, S32 height, LLTex
}
gGL.end();
}
- gGL.popMatrix();
+ gGL.popUIMatrix();
if (solid_color)
{
@@ -660,39 +668,72 @@ void gl_draw_scaled_rotated_image(S32 x, S32 y, S32 width, S32 height, F32 degre
LLGLSUIDefault gls_ui;
- gGL.pushMatrix();
+
+ gGL.getTexUnit(0)->bind(image);
+
+ gGL.color4fv(color.mV);
+
+ if (degrees == 0.f)
{
- gGL.translatef((F32)x, (F32)y, 0.f);
- if( degrees )
+ gGL.pushUIMatrix();
+ gGL.translateUI((F32)x, (F32)y, 0.f);
+
+ gGL.begin(LLRender::QUADS);
{
- F32 offset_x = F32(width/2);
- F32 offset_y = F32(height/2);
- gGL.translatef( offset_x, offset_y, 0.f);
- glRotatef( degrees, 0.f, 0.f, 1.f );
- gGL.translatef( -offset_x, -offset_y, 0.f );
+ gGL.texCoord2f(uv_rect.mRight, uv_rect.mTop);
+ gGL.vertex2i(width, height );
+
+ gGL.texCoord2f(uv_rect.mLeft, uv_rect.mTop);
+ gGL.vertex2i(0, height );
+
+ gGL.texCoord2f(uv_rect.mLeft, uv_rect.mBottom);
+ gGL.vertex2i(0, 0);
+
+ gGL.texCoord2f(uv_rect.mRight, uv_rect.mBottom);
+ gGL.vertex2i(width, 0);
}
+ gGL.end();
+ gGL.popUIMatrix();
+ }
+ else
+ {
+ gGL.pushUIMatrix();
+ gGL.translateUI((F32)x, (F32)y, 0.f);
+
+ F32 offset_x = F32(width/2);
+ F32 offset_y = F32(height/2);
+
+ gGL.translateUI(offset_x, offset_y, 0.f);
+ LLMatrix3 quat(0.f, 0.f, degrees*DEG_TO_RAD);
+
gGL.getTexUnit(0)->bind(image);
gGL.color4fv(color.mV);
gGL.begin(LLRender::QUADS);
{
+ LLVector3 v;
+
+ v = LLVector3(offset_x, offset_y, 0.f) * quat;
gGL.texCoord2f(uv_rect.mRight, uv_rect.mTop);
- gGL.vertex2i(width, height );
+ gGL.vertex2f(v.mV[0], v.mV[1] );
+ v = LLVector3(-offset_x, offset_y, 0.f) * quat;
gGL.texCoord2f(uv_rect.mLeft, uv_rect.mTop);
- gGL.vertex2i(0, height );
+ gGL.vertex2f(v.mV[0], v.mV[1] );
+ v = LLVector3(-offset_x, -offset_y, 0.f) * quat;
gGL.texCoord2f(uv_rect.mLeft, uv_rect.mBottom);
- gGL.vertex2i(0, 0);
+ gGL.vertex2f(v.mV[0], v.mV[1] );
+ v = LLVector3(offset_x, -offset_y, 0.f) * quat;
gGL.texCoord2f(uv_rect.mRight, uv_rect.mBottom);
- gGL.vertex2i(width, 0);
+ gGL.vertex2f(v.mV[0], v.mV[1] );
}
gGL.end();
+ gGL.popUIMatrix();
}
- gGL.popMatrix();
}
@@ -747,9 +788,9 @@ void gl_arc_2d(F32 center_x, F32 center_y, F32 radius, S32 steps, BOOL filled, F
end_angle += F_TWO_PI;
}
- gGL.pushMatrix();
+ gGL.pushUIMatrix();
{
- gGL.translatef(center_x, center_y, 0.f);
+ gGL.translateUI(center_x, center_y, 0.f);
// Inexact, but reasonably fast.
F32 delta = (end_angle - start_angle) / steps;
@@ -780,15 +821,15 @@ void gl_arc_2d(F32 center_x, F32 center_y, F32 radius, S32 steps, BOOL filled, F
}
gGL.end();
}
- gGL.popMatrix();
+ gGL.popUIMatrix();
}
void gl_circle_2d(F32 center_x, F32 center_y, F32 radius, S32 steps, BOOL filled)
{
- gGL.pushMatrix();
+ gGL.pushUIMatrix();
{
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
- gGL.translatef(center_x, center_y, 0.f);
+ gGL.translateUI(center_x, center_y, 0.f);
// Inexact, but reasonably fast.
F32 delta = F_TWO_PI / steps;
@@ -819,7 +860,7 @@ void gl_circle_2d(F32 center_x, F32 center_y, F32 radius, S32 steps, BOOL filled
}
gGL.end();
}
- gGL.popMatrix();
+ gGL.popUIMatrix();
}
// Renders a ring with sides (tube shape)
@@ -846,9 +887,9 @@ void gl_deep_circle( F32 radius, F32 depth, S32 steps )
void gl_ring( F32 radius, F32 width, const LLColor4& center_color, const LLColor4& side_color, S32 steps, BOOL render_center )
{
- gGL.pushMatrix();
+ gGL.pushUIMatrix();
{
- gGL.translatef(0.f, 0.f, -width / 2);
+ gGL.translateUI(0.f, 0.f, -width / 2);
if( render_center )
{
gGL.color4fv(center_color.mV);
@@ -857,11 +898,11 @@ void gl_ring( F32 radius, F32 width, const LLColor4& center_color, const LLColor
else
{
gl_washer_2d(radius, radius - width, steps, side_color, side_color);
- gGL.translatef(0.f, 0.f, width);
+ gGL.translateUI(0.f, 0.f, width);
gl_washer_2d(radius - width, radius, steps, side_color, side_color);
}
}
- gGL.popMatrix();
+ gGL.popUIMatrix();
}
// Draw gray and white checkerboard with black border
@@ -1050,9 +1091,9 @@ void gl_segmented_rect_2d_tex(const S32 left,
S32 width = llabs(right - left);
S32 height = llabs(top - bottom);
- gGL.pushMatrix();
+ gGL.pushUIMatrix();
- gGL.translatef((F32)left, (F32)bottom, 0.f);
+ gGL.translateUI((F32)left, (F32)bottom, 0.f);
LLVector2 border_uv_scale((F32)border_size / (F32)texture_width, (F32)border_size / (F32)texture_height);
if (border_uv_scale.mV[VX] > 0.5f)
@@ -1193,7 +1234,7 @@ void gl_segmented_rect_2d_tex(const S32 left,
}
gGL.end();
- gGL.popMatrix();
+ gGL.popUIMatrix();
}
void gl_segmented_rect_2d_fragment_tex(const S32 left,
@@ -1210,9 +1251,9 @@ void gl_segmented_rect_2d_fragment_tex(const S32 left,
S32 width = llabs(right - left);
S32 height = llabs(top - bottom);
- gGL.pushMatrix();
+ gGL.pushUIMatrix();
- gGL.translatef((F32)left, (F32)bottom, 0.f);
+ gGL.translateUI((F32)left, (F32)bottom, 0.f);
LLVector2 border_uv_scale((F32)border_size / (F32)texture_width, (F32)border_size / (F32)texture_height);
if (border_uv_scale.mV[VX] > 0.5f)
@@ -1383,7 +1424,7 @@ void gl_segmented_rect_2d_fragment_tex(const S32 left,
}
gGL.end();
- gGL.popMatrix();
+ gGL.popUIMatrix();
}
void gl_segmented_rect_3d_tex(const LLVector2& border_scale, const LLVector3& border_width,
@@ -1573,6 +1614,13 @@ void LLUI::cleanupClass()
sImageProvider->cleanUp();
}
+void LLUI::setPopupFuncs(const add_popup_t& add_popup, const remove_popup_t& remove_popup, const clear_popups_t& clear_popups)
+{
+ sAddPopupFunc = add_popup;
+ sRemovePopupFunc = remove_popup;
+ sClearPopupsFunc = clear_popups;
+}
+
//static
void LLUI::dirtyRect(LLRect rect)
{
@@ -1591,7 +1639,7 @@ void LLUI::dirtyRect(LLRect rect)
//static
void LLUI::translate(F32 x, F32 y, F32 z)
{
- gGL.translatef(x,y,z);
+ gGL.translateUI(x,y,z);
LLFontGL::sCurOrigin.mX += (S32) x;
LLFontGL::sCurOrigin.mY += (S32) y;
LLFontGL::sCurOrigin.mZ += z;
@@ -1600,14 +1648,14 @@ void LLUI::translate(F32 x, F32 y, F32 z)
//static
void LLUI::pushMatrix()
{
- gGL.pushMatrix();
+ gGL.pushUIMatrix();
LLFontGL::sOriginStack.push_back(LLFontGL::sCurOrigin);
}
//static
void LLUI::popMatrix()
{
- gGL.popMatrix();
+ gGL.popUIMatrix();
LLFontGL::sCurOrigin = *LLFontGL::sOriginStack.rbegin();
LLFontGL::sOriginStack.pop_back();
}
@@ -1615,7 +1663,7 @@ void LLUI::popMatrix()
//static
void LLUI::loadIdentity()
{
- glLoadIdentity();
+ gGL.loadUIIdentity();
LLFontGL::sCurOrigin.mX = 0;
LLFontGL::sCurOrigin.mY = 0;
LLFontGL::sCurOrigin.mZ = 0;
@@ -1843,6 +1891,39 @@ LLControlGroup& LLUI::getControlControlGroup (const std::string& controlname)
return *sSettingGroups["config"]; // default group
}
+//static
+void LLUI::addPopup(LLView* viewp)
+{
+ if (sAddPopupFunc)
+ {
+ sAddPopupFunc(viewp);
+ }
+}
+
+//static
+void LLUI::removePopup(LLView* viewp)
+{
+ if (sRemovePopupFunc)
+ {
+ sRemovePopupFunc(viewp);
+ }
+}
+
+//static
+void LLUI::clearPopups()
+{
+ if (sClearPopupsFunc)
+ {
+ sClearPopupsFunc();
+ }
+}
+
+//static
+void LLUI::reportBadKeystroke()
+{
+ make_ui_sound("UISndBadKeystroke");
+}
+
//static
// spawn_x and spawn_y are top left corner of view in screen GL coordinates
void LLUI::positionViewNearMouse(LLView* view, S32 spawn_x, S32 spawn_y)
@@ -1894,7 +1975,9 @@ namespace LLInitParam
blue("blue"),
alpha("alpha"),
control("")
- {}
+ {
+ setBlockFromValue();
+ }
void TypedParam<LLUIColor>::setValueFromBlock() const
{
@@ -1911,10 +1994,10 @@ namespace LLInitParam
void TypedParam<LLUIColor>::setBlockFromValue()
{
LLColor4 color = mData.mValue.get();
- red = color.mV[VRED];
- green = color.mV[VGREEN];
- blue = color.mV[VBLUE];
- alpha = color.mV[VALPHA];
+ red.set(color.mV[VRED], false);
+ green.set(color.mV[VGREEN], false);
+ blue.set(color.mV[VBLUE], false);
+ alpha.set(color.mV[VALPHA], false);
control.set("", false);
}
@@ -1939,7 +2022,9 @@ namespace LLInitParam
size("size"),
style("style")
{
+ setBlockFromValue();
addSynonym(name, "");
+ setBlockFromValue();
}
void TypedParam<const LLFontGL*>::setValueFromBlock() const
@@ -1965,9 +2050,9 @@ namespace LLInitParam
{
if (mData.mValue)
{
- name = LLFontGL::nameFromFont(mData.mValue);
- size = LLFontGL::sizeFromFont(mData.mValue);
- style = LLFontGL::getStringFromStyle(mData.mValue->getFontDesc().getStyle());
+ name.set(LLFontGL::nameFromFont(mData.mValue), false);
+ size.set(LLFontGL::sizeFromFont(mData.mValue), false);
+ style.set(LLFontGL::getStringFromStyle(mData.mValue->getFontDesc().getStyle()), false);
}
}
@@ -1979,7 +2064,9 @@ namespace LLInitParam
bottom("bottom"),
width("width"),
height("height")
- {}
+ {
+ setBlockFromValue();
+ }
void TypedParam<LLRect>::setValueFromBlock() const
{
@@ -2064,6 +2151,7 @@ namespace LLInitParam
x("x"),
y("y")
{
+ setBlockFromValue();
}
void TypedParam<LLCoordGL>::setValueFromBlock() const
@@ -2073,8 +2161,8 @@ namespace LLInitParam
void TypedParam<LLCoordGL>::setBlockFromValue()
{
- x = mData.mValue.mX;
- y = mData.mValue.mY;
+ x.set(mData.mValue.mX, false);
+ y.set(mData.mValue.mY, false);
}
diff --git a/indra/llui/llui.h b/indra/llui/llui.h
index 5840e76f5c..c18262ef76 100644
--- a/indra/llui/llui.h
+++ b/indra/llui/llui.h
@@ -160,12 +160,17 @@ public:
// Methods
//
typedef std::map<std::string, LLControlGroup*> settings_map_t;
+ typedef boost::function<void(LLView*)> add_popup_t;
+ typedef boost::function<void(LLView*)> remove_popup_t;
+ typedef boost::function<void(void)> clear_popups_t;
+
static void initClass(const settings_map_t& settings,
LLImageProviderInterface* image_provider,
LLUIAudioCallback audio_callback = NULL,
const LLVector2 *scale_factor = NULL,
const std::string& language = LLStringUtil::null);
static void cleanupClass();
+ static void setPopupFuncs(const add_popup_t& add_popup, const remove_popup_t&, const clear_popups_t& );
static void pushMatrix();
static void popMatrix();
@@ -208,6 +213,12 @@ public:
static void resetMouseIdleTimer() { sMouseIdleTimer.reset(); }
static LLWindow* getWindow() { return sWindow; }
+ static void addPopup(LLView*);
+ static void removePopup(LLView*);
+ static void clearPopups();
+
+ static void reportBadKeystroke();
+
// Ensures view does not overlap mouse cursor, but is inside
// the view's parent rectangle. Used for tooltips, inspectors.
// Optionally override the view's default X/Y, which are relative to the
@@ -227,6 +238,9 @@ private:
static LLImageProviderInterface* sImageProvider;
static std::vector<std::string> sXUIPaths;
static LLFrameTimer sMouseIdleTimer;
+ static add_popup_t sAddPopupFunc;
+ static remove_popup_t sRemovePopupFunc;
+ static clear_popups_t sClearPopupsFunc;
};
@@ -426,8 +440,8 @@ namespace LLInitParam
{
typedef BlockValue<const LLFontGL*> super_t;
public:
- Mandatory<std::string> name;
- Optional<std::string> size,
+ Optional<std::string> name,
+ size,
style;
TypedParam(BlockDescriptor& descriptor, const char* name, const LLFontGL* const value, ParamDescriptor::validation_func_t func, S32 min_count, S32 max_count);
diff --git a/indra/llui/lluicolortable.cpp b/indra/llui/lluicolortable.cpp
index 9be33483d0..1b64ef3abe 100644
--- a/indra/llui/lluicolortable.cpp
+++ b/indra/llui/lluicolortable.cpp
@@ -56,7 +56,7 @@ LLUIColorTable::Params::Params()
{
}
-void LLUIColorTable::insertFromParams(const Params& p)
+void LLUIColorTable::insertFromParams(const Params& p, string_color_map_t& table)
{
// this map will contain all color references after the following loop
typedef std::map<std::string, std::string> string_string_map_t;
@@ -69,14 +69,7 @@ void LLUIColorTable::insertFromParams(const Params& p)
ColorEntryParams color_entry = *it;
if(color_entry.color.value.isChosen())
{
- if(mUserSetColors.find(color_entry.name)!=mUserSetColors.end())
- {
- setColor(color_entry.name, color_entry.color.value);
- }
- else
- {
- setColor(color_entry.name, color_entry.color.value, mLoadedColors);
- }
+ setColor(color_entry.name, color_entry.color.value, table);
}
else
{
@@ -220,16 +213,16 @@ bool LLUIColorTable::loadFromSettings()
bool result = false;
std::string default_filename = gDirUtilp->getExpandedFilename(LL_PATH_DEFAULT_SKIN, "colors.xml");
- result |= loadFromFilename(default_filename);
+ result |= loadFromFilename(default_filename, mLoadedColors);
std::string current_filename = gDirUtilp->getExpandedFilename(LL_PATH_TOP_SKIN, "colors.xml");
if(current_filename != default_filename)
{
- result |= loadFromFilename(current_filename);
+ result |= loadFromFilename(current_filename, mLoadedColors);
}
std::string user_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "colors.xml");
- loadFromFilename(user_filename);
+ loadFromFilename(user_filename, mUserSetColors);
return result;
}
@@ -299,7 +292,7 @@ void LLUIColorTable::setColor(const std::string& name, const LLColor4& color, st
}
}
-bool LLUIColorTable::loadFromFilename(const std::string& filename)
+bool LLUIColorTable::loadFromFilename(const std::string& filename, string_color_map_t& table)
{
LLXMLNodePtr root;
@@ -320,7 +313,7 @@ bool LLUIColorTable::loadFromFilename(const std::string& filename)
if(params.validateBlock())
{
- insertFromParams(params);
+ insertFromParams(params, table);
}
else
{
@@ -330,3 +323,11 @@ bool LLUIColorTable::loadFromFilename(const std::string& filename)
return true;
}
+
+void LLUIColorTable::insertFromParams(const Params& p)
+{
+ insertFromParams(p, mUserSetColors);
+}
+
+// EOF
+
diff --git a/indra/llui/lluicolortable.h b/indra/llui/lluicolortable.h
index 59be0c4f9a..d401e5e724 100644
--- a/indra/llui/lluicolortable.h
+++ b/indra/llui/lluicolortable.h
@@ -45,6 +45,10 @@ class LLUIColor;
class LLUIColorTable : public LLSingleton<LLUIColorTable>
{
LOG_CLASS(LLUIColorTable);
+
+ // consider using sorted vector, can be much faster
+ typedef std::map<std::string, LLUIColor> string_color_map_t;
+
public:
struct ColorParams : LLInitParam::Choice<ColorParams>
{
@@ -91,10 +95,9 @@ public:
void saveUserSettings() const;
private:
- bool loadFromFilename(const std::string& filename);
+ bool loadFromFilename(const std::string& filename, string_color_map_t& table);
- // consider using sorted vector, can be much faster
- typedef std::map<std::string, LLColor4> string_color_map_t;
+ void insertFromParams(const Params& p, string_color_map_t& table);
void clearTable(string_color_map_t& table);
void setColor(const std::string& name, const LLColor4& color, string_color_map_t& table);
diff --git a/indra/llui/lluictrlfactory.cpp b/indra/llui/lluictrlfactory.cpp
index 27237800d4..930dadc377 100644
--- a/indra/llui/lluictrlfactory.cpp
+++ b/indra/llui/lluictrlfactory.cpp
@@ -435,7 +435,10 @@ void LLUICtrlFactory::registerWidget(const std::type_info* widget_type, const st
std::string* existing_tag = LLWidgetNameRegistry::instance().getValue(param_block_type);
if (existing_tag != NULL && *existing_tag != tag)
{
- llerrs << "Duplicate entry for T::Params, try creating empty param block in derived classes that inherit T::Params" << llendl;
+ std::cerr << "Duplicate entry for T::Params, try creating empty param block in derived classes that inherit T::Params" << std::endl;
+ // forcing crash here
+ char* foo = 0;
+ *foo = 1;
}
LLWidgetNameRegistry ::instance().defaultRegistrar().add(param_block_type, tag);
// associate widget type with factory function
diff --git a/indra/llui/lluifwd.h b/indra/llui/lluifwd.h
index f99bb39fdd..d6047b943c 100644
--- a/indra/llui/lluifwd.h
+++ b/indra/llui/lluifwd.h
@@ -39,6 +39,7 @@ class LLComboBox;
class LLDragHandle;
class LLFloater;
class LLIconCtrl;
+class LLLoadingIndicator;
class LLLineEditor;
class LLMenuGL;
class LLPanel;
diff --git a/indra/llui/lluiimage.cpp b/indra/llui/lluiimage.cpp
index 966d919dc7..8cd6460b66 100644
--- a/indra/llui/lluiimage.cpp
+++ b/indra/llui/lluiimage.cpp
@@ -182,11 +182,11 @@ namespace LLInitParam
{
if (mData.mValue == NULL)
{
- name = "none";
+ name.set("none", false);
}
else
{
- name = mData.mValue->getName();
+ name.set(mData.mValue->getName(), false);
}
}
diff --git a/indra/llui/lluiimage.h b/indra/llui/lluiimage.h
index bdfc44262d..4ea0738026 100644
--- a/indra/llui/lluiimage.h
+++ b/indra/llui/lluiimage.h
@@ -109,6 +109,7 @@ namespace LLInitParam
TypedParam(BlockDescriptor& descriptor, const char* name, super_t::value_assignment_t value, ParamDescriptor::validation_func_t func, S32 min_count, S32 max_count)
: super_t(descriptor, name, value, func, min_count, max_count)
{
+ setBlockFromValue();
}
void setValueFromBlock() const;
diff --git a/indra/llui/lluistring.h b/indra/llui/lluistring.h
index 7ec0fd603a..32cfc0d9cd 100644
--- a/indra/llui/lluistring.h
+++ b/indra/llui/lluistring.h
@@ -64,7 +64,7 @@ class LLUIString
public:
// These methods all perform appropriate argument substitution
// and modify mOrig where appropriate
- LLUIString() {}
+ LLUIString() : mNeedsResult(false), mNeedsWResult(false) {}
LLUIString(const std::string& instring, const LLStringUtil::format_map_t& args);
LLUIString(const std::string& instring) { assign(instring); }
diff --git a/indra/llui/llurlaction.cpp b/indra/llui/llurlaction.cpp
index 679db5e39b..2f13a56b42 100644
--- a/indra/llui/llurlaction.cpp
+++ b/indra/llui/llurlaction.cpp
@@ -146,3 +146,20 @@ void LLUrlAction::copyLabelToClipboard(std::string url)
LLView::getWindow()->copyTextToClipboard(utf8str_to_wstring(match.getLabel()));
}
}
+
+void LLUrlAction::showProfile(std::string url)
+{
+ // Get id from 'secondlife:///app/{cmd}/{id}/{action}'
+ // and show its profile
+ 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))
+ {
+ std::string cmd_str = path_array.get(1).asString();
+ executeSLURL("secondlife:///app/" + cmd_str + "/" + id_str + "/about");
+ }
+ }
+}
diff --git a/indra/llui/llurlaction.h b/indra/llui/llurlaction.h
index 4830cf27ef..b96faf1b3f 100644
--- a/indra/llui/llurlaction.h
+++ b/indra/llui/llurlaction.h
@@ -79,6 +79,9 @@ public:
/// copy a Url to the clipboard
static void copyURLToClipboard(std::string url);
+ /// if the Url specifies an SL command in the form like 'app/{cmd}/{id}/*', show its profile
+ static void showProfile(std::string url);
+
/// specify the callbacks to enable this class's functionality
static void setOpenURLCallback(void (*cb) (const std::string& url));
static void setOpenURLInternalCallback(void (*cb) (const std::string& url));
diff --git a/indra/llui/llurlentry.cpp b/indra/llui/llurlentry.cpp
index 4927e57a52..3375c13c94 100644
--- a/indra/llui/llurlentry.cpp
+++ b/indra/llui/llurlentry.cpp
@@ -34,13 +34,16 @@
#include "linden_common.h"
#include "llurlentry.h"
#include "lluri.h"
+#include "llurlmatch.h"
+#include "llurlregistry.h"
#include "llcachename.h"
#include "lltrans.h"
#include "lluicolortable.h"
-LLUrlEntryBase::LLUrlEntryBase()
-: mColor(LLUIColorTable::instance().getColor("HTMLLinkColor"))
+LLUrlEntryBase::LLUrlEntryBase() :
+ mColor(LLUIColorTable::instance().getColor("HTMLLinkColor")),
+ mDisabledLink(false)
{
}
@@ -48,7 +51,7 @@ LLUrlEntryBase::~LLUrlEntryBase()
{
}
-std::string LLUrlEntryBase::getUrl(const std::string &string)
+std::string LLUrlEntryBase::getUrl(const std::string &string) const
{
return escapeUrl(string);
}
@@ -88,7 +91,7 @@ std::string LLUrlEntryBase::escapeUrl(const std::string &url) const
return LLURI::escape(url, no_escape_chars, true);
}
-std::string LLUrlEntryBase::getLabelFromWikiLink(const std::string &url)
+std::string LLUrlEntryBase::getLabelFromWikiLink(const std::string &url) const
{
// return the label part from [http://www.example.org Label]
const char *text = url.c_str();
@@ -104,7 +107,7 @@ std::string LLUrlEntryBase::getLabelFromWikiLink(const std::string &url)
return unescapeUrl(url.substr(start, url.size()-start-1));
}
-std::string LLUrlEntryBase::getUrlFromWikiLink(const std::string &string)
+std::string LLUrlEntryBase::getUrlFromWikiLink(const std::string &string) const
{
// return the url part from [http://www.example.org Label]
const char *text = string.c_str();
@@ -191,7 +194,7 @@ std::string LLUrlEntryHTTPLabel::getLabel(const std::string &url, const LLUrlLab
return getLabelFromWikiLink(url);
}
-std::string LLUrlEntryHTTPLabel::getUrl(const std::string &string)
+std::string LLUrlEntryHTTPLabel::getUrl(const std::string &string) const
{
return getUrlFromWikiLink(string);
}
@@ -204,7 +207,7 @@ LLUrlEntryHTTPNoProtocol::LLUrlEntryHTTPNoProtocol()
mPattern = boost::regex("("
"\\bwww\\.\\S+\\.\\S+" // i.e. www.FOO.BAR
"|" // or
- "(?<!@)\\b[^[:space:]:@/]+\\.(?:com|net|edu|org)([/:]\\S*)?\\b" // i.e. FOO.net
+ "(?<!@)\\b[^[:space:]:@/>]+\\.(?:com|net|edu|org)([/:][^[:space:]<]*)?\\b" // i.e. FOO.net
")",
boost::regex::perl|boost::regex::icase);
mMenuName = "menu_url_http.xml";
@@ -216,7 +219,7 @@ std::string LLUrlEntryHTTPNoProtocol::getLabel(const std::string &url, const LLU
return unescapeUrl(url);
}
-std::string LLUrlEntryHTTPNoProtocol::getUrl(const std::string &string)
+std::string LLUrlEntryHTTPNoProtocol::getUrl(const std::string &string) const
{
if (string.find("://") == std::string::npos)
{
@@ -231,7 +234,7 @@ std::string LLUrlEntryHTTPNoProtocol::getUrl(const std::string &string)
LLUrlEntrySLURL::LLUrlEntrySLURL()
{
// see http://slurl.com/about.php for details on the SLURL format
- mPattern = boost::regex("http://slurl.com/secondlife/\\S+/?(\\d+)?/?(\\d+)?/?(\\d+)?/?\\S*",
+ mPattern = boost::regex("http://(maps.secondlife.com|slurl.com)/secondlife/[^ /]+(/\\d+){0,3}(/?(\\?title|\\?img|\\?msg)=\\S*)?/?",
boost::regex::perl|boost::regex::icase);
mMenuName = "menu_url_slurl.xml";
mTooltip = LLTrans::getString("TooltipSLURL");
@@ -286,7 +289,7 @@ std::string LLUrlEntrySLURL::getLabel(const std::string &url, const LLUrlLabelCa
std::string LLUrlEntrySLURL::getLocation(const std::string &url) const
{
// return the part of the Url after slurl.com/secondlife/
- const std::string search_string = "secondlife";
+ const std::string search_string = "/secondlife";
size_t pos = url.find(search_string);
if (pos == std::string::npos)
{
@@ -319,6 +322,38 @@ void LLUrlEntryAgent::onAgentNameReceived(const LLUUID& id,
callObservers(id.asString(), first + " " + last);
}
+std::string LLUrlEntryAgent::getTooltip(const std::string &string) const
+{
+ // return a tooltip corresponding to the URL type instead of the generic one
+ std::string url = getUrl(string);
+
+ if (LLStringUtil::endsWith(url, "/mute"))
+ {
+ return LLTrans::getString("TooltipAgentMute");
+ }
+ if (LLStringUtil::endsWith(url, "/unmute"))
+ {
+ return LLTrans::getString("TooltipAgentUnmute");
+ }
+ if (LLStringUtil::endsWith(url, "/im"))
+ {
+ return LLTrans::getString("TooltipAgentIM");
+ }
+ if (LLStringUtil::endsWith(url, "/pay"))
+ {
+ return LLTrans::getString("TooltipAgentPay");
+ }
+ if (LLStringUtil::endsWith(url, "/offerteleport"))
+ {
+ return LLTrans::getString("TooltipAgentOfferTeleport");
+ }
+ if (LLStringUtil::endsWith(url, "/requestfriend"))
+ {
+ return LLTrans::getString("TooltipAgentRequestFriend");
+ }
+ return LLTrans::getString("TooltipAgentUrl");
+}
+
std::string LLUrlEntryAgent::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
{
if (!gCacheName)
@@ -342,6 +377,31 @@ std::string LLUrlEntryAgent::getLabel(const std::string &url, const LLUrlLabelCa
}
else if (gCacheName->getFullName(agent_id, full_name))
{
+ // customize label string based on agent SLapp suffix
+ if (LLStringUtil::endsWith(url, "/mute"))
+ {
+ return LLTrans::getString("SLappAgentMute") + " " + full_name;
+ }
+ if (LLStringUtil::endsWith(url, "/unmute"))
+ {
+ return LLTrans::getString("SLappAgentUnmute") + " " + full_name;
+ }
+ if (LLStringUtil::endsWith(url, "/im"))
+ {
+ return LLTrans::getString("SLappAgentIM") + " " + full_name;
+ }
+ if (LLStringUtil::endsWith(url, "/pay"))
+ {
+ return LLTrans::getString("SLappAgentPay") + " " + full_name;
+ }
+ if (LLStringUtil::endsWith(url, "/offerteleport"))
+ {
+ return LLTrans::getString("SLappAgentOfferTeleport") + " " + full_name;
+ }
+ if (LLStringUtil::endsWith(url, "/requestfriend"))
+ {
+ return LLTrans::getString("SLappAgentRequestFriend") + " " + full_name;
+ }
return full_name;
}
else
@@ -433,6 +493,35 @@ std::string LLUrlEntryInventory::getLabel(const std::string &url, const LLUrlLab
return LLURI::unescape(label.empty() ? url : label);
}
+//
+// LLUrlEntryObjectIM Describes a Second Life inspector for the object Url, e.g.,
+// secondlife:///app/objectim/7bcd7864-da6b-e43f-4486-91d28a28d95b?name=Object&owner=3de548e1-57be-cfea-2b78-83ae3ad95998&slurl=Danger!%20Danger!/200/200/30/&groupowned=1
+//
+LLUrlEntryObjectIM::LLUrlEntryObjectIM()
+{
+ mPattern = boost::regex("secondlife:///app/objectim/[\\da-f-]+\?.*",
+ boost::regex::perl|boost::regex::icase);
+ mMenuName = "menu_url_objectim.xml";
+}
+
+std::string LLUrlEntryObjectIM::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+{
+ LLURI uri(url);
+ LLSD query_map = uri.queryMap();
+ if (query_map.has("name"))
+ return query_map["name"];
+ return unescapeUrl(url);
+}
+
+std::string LLUrlEntryObjectIM::getLocation(const std::string &url) const
+{
+ LLURI uri(url);
+ LLSD query_map = uri.queryMap();
+ if (query_map.has("slurl"))
+ return query_map["slurl"];
+ return LLUrlEntryBase::getLocation(url);
+}
+
///
/// LLUrlEntryParcel Describes a Second Life parcel Url, e.g.,
/// secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about
@@ -596,11 +685,25 @@ std::string LLUrlEntrySLLabel::getLabel(const std::string &url, const LLUrlLabel
return getLabelFromWikiLink(url);
}
-std::string LLUrlEntrySLLabel::getUrl(const std::string &string)
+std::string LLUrlEntrySLLabel::getUrl(const std::string &string) const
{
return getUrlFromWikiLink(string);
}
+std::string LLUrlEntrySLLabel::getTooltip(const std::string &string) const
+{
+ // return a tooltip corresponding to the URL type instead of the generic one (EXT-4574)
+ std::string url = getUrl(string);
+ LLUrlMatch match;
+ if (LLUrlRegistry::instance().findUrl(url, match))
+ {
+ return match.getTooltip();
+ }
+
+ // unrecognized URL? should not happen
+ return LLUrlEntryBase::getTooltip(string);
+}
+
//
// LLUrlEntryWorldMap Describes secondlife:///<location> URLs
//
@@ -629,7 +732,7 @@ std::string LLUrlEntryWorldMap::getLabel(const std::string &url, const LLUrlLabe
}
const std::string label = LLTrans::getString("SLurlLabelShowOnMap");
- std::string location = path_array[2];
+ std::string location = unescapeUrl(path_array[2]);
std::string x = (path_parts > 3) ? path_array[3] : "128";
std::string y = (path_parts > 4) ? path_array[4] : "128";
std::string z = (path_parts > 5) ? path_array[5] : "0";
@@ -641,3 +744,56 @@ std::string LLUrlEntryWorldMap::getLocation(const std::string &url) const
// return the part of the Url after secondlife:///app/worldmap/ part
return ::getStringAfterToken(url, "app/worldmap/");
}
+
+//
+// LLUrlEntryNoLink lets us turn of URL detection with <nolink>...</nolink> tags
+//
+LLUrlEntryNoLink::LLUrlEntryNoLink()
+{
+ mPattern = boost::regex("<nolink>[^<]*</nolink>",
+ boost::regex::perl|boost::regex::icase);
+ mDisabledLink = true;
+}
+
+std::string LLUrlEntryNoLink::getUrl(const std::string &url) const
+{
+ // return the text between the <nolink> and </nolink> tags
+ return url.substr(8, url.size()-8-9);
+}
+
+std::string LLUrlEntryNoLink::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+{
+ return getUrl(url);
+}
+
+//
+// LLUrlEntryIcon describes an icon with <icon>...</icon> tags
+//
+LLUrlEntryIcon::LLUrlEntryIcon()
+{
+ mPattern = boost::regex("<icon\\s*>\\s*([^<]*)?\\s*</icon\\s*>",
+ boost::regex::perl|boost::regex::icase);
+ mDisabledLink = true;
+}
+
+std::string LLUrlEntryIcon::getUrl(const std::string &url) const
+{
+ return LLStringUtil::null;
+}
+
+std::string LLUrlEntryIcon::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+{
+ return LLStringUtil::null;
+}
+
+std::string LLUrlEntryIcon::getIcon(const std::string &url)
+{
+ // Grep icon info between <icon>...</icon> tags
+ // matches[1] contains the icon name/path
+ boost::match_results<std::string::const_iterator> matches;
+ mIcon = (boost::regex_match(url, matches, mPattern) && matches[1].matched)
+ ? matches[1]
+ : LLStringUtil::null;
+ LLStringUtil::trim(mIcon);
+ return mIcon;
+}
diff --git a/indra/llui/llurlentry.h b/indra/llui/llurlentry.h
index 4adffde99c..71f030677a 100644
--- a/indra/llui/llurlentry.h
+++ b/indra/llui/llurlentry.h
@@ -71,19 +71,19 @@ public:
boost::regex getPattern() const { return mPattern; }
/// Return the url from a string that matched the regex
- virtual std::string getUrl(const std::string &string);
+ virtual std::string getUrl(const std::string &string) const;
/// Given a matched Url, return a label for the Url
virtual std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) { return url; }
/// Return an icon that can be displayed next to Urls of this type
- std::string getIcon() const { return mIcon; }
+ virtual std::string getIcon(const std::string &url) { return mIcon; }
/// Return the color to render the displayed text
LLUIColor getColor() const { return mColor; }
/// Given a matched Url, return a tooltip string for the hyperlink
- std::string getTooltip() const { return mTooltip; }
+ virtual std::string getTooltip(const std::string &string) const { return mTooltip; }
/// Return the name of a XUI file containing the context menu items
std::string getMenuName() const { return mMenuName; }
@@ -91,12 +91,15 @@ public:
/// Return the name of a SL location described by this Url, if any
virtual std::string getLocation(const std::string &url) const { return ""; }
+ /// is this a match for a URL that should not be hyperlinked?
+ bool isLinkDisabled() const { return mDisabledLink; }
+
protected:
std::string getIDStringFromUrl(const std::string &url) const;
std::string escapeUrl(const std::string &url) const;
std::string unescapeUrl(const std::string &url) const;
- std::string getLabelFromWikiLink(const std::string &url);
- std::string getUrlFromWikiLink(const std::string &string);
+ std::string getLabelFromWikiLink(const std::string &url) const;
+ std::string getUrlFromWikiLink(const std::string &string) const;
void addObserver(const std::string &id, const std::string &url, const LLUrlLabelCallback &cb);
void callObservers(const std::string &id, const std::string &label);
@@ -111,6 +114,7 @@ protected:
std::string mTooltip;
LLUIColor mColor;
std::multimap<std::string, LLUrlEntryObserver> mObservers;
+ bool mDisabledLink;
};
///
@@ -131,7 +135,7 @@ class LLUrlEntryHTTPLabel : public LLUrlEntryBase
public:
LLUrlEntryHTTPLabel();
/*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
- /*virtual*/ std::string getUrl(const std::string &string);
+ /*virtual*/ std::string getUrl(const std::string &string) const;
};
///
@@ -142,7 +146,7 @@ class LLUrlEntryHTTPNoProtocol : public LLUrlEntryBase
public:
LLUrlEntryHTTPNoProtocol();
/*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
- /*virtual*/ std::string getUrl(const std::string &string);
+ /*virtual*/ std::string getUrl(const std::string &string) const;
};
///
@@ -165,6 +169,7 @@ class LLUrlEntryAgent : public LLUrlEntryBase
public:
LLUrlEntryAgent();
/*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
+ /*virtual*/ std::string getTooltip(const std::string &string) const;
private:
void onAgentNameReceived(const LLUUID& id, const std::string& first,
const std::string& last, BOOL is_group);
@@ -196,6 +201,18 @@ public:
private:
};
+///
+/// LLUrlEntryObjectIM Describes a Second Life inspector for the object Url, e.g.,
+/// secondlife:///app/objectim/7bcd7864-da6b-e43f-4486-91d28a28d95b?name=Object&owner=3de548e1-57be-cfea-2b78-83ae3ad95998&slurl=Danger!%20Danger!/200/200/30/&groupowned=1
+///
+class LLUrlEntryObjectIM : public LLUrlEntryBase
+{
+public:
+ LLUrlEntryObjectIM();
+ /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
+ /*virtual*/ std::string getLocation(const std::string &url) const;
+private:
+};
///
/// LLUrlEntryParcel Describes a Second Life parcel Url, e.g.,
@@ -252,7 +269,8 @@ class LLUrlEntrySLLabel : public LLUrlEntryBase
public:
LLUrlEntrySLLabel();
/*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
- /*virtual*/ std::string getUrl(const std::string &string);
+ /*virtual*/ std::string getUrl(const std::string &string) const;
+ /*virtual*/ std::string getTooltip(const std::string &string) const;
};
///
@@ -267,4 +285,28 @@ public:
/*virtual*/ std::string getLocation(const std::string &url) const;
};
+///
+/// LLUrlEntryNoLink lets us turn of URL detection with <nolink>...</nolink> tags
+///
+class LLUrlEntryNoLink : public LLUrlEntryBase
+{
+public:
+ LLUrlEntryNoLink();
+ /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
+ /*virtual*/ std::string getUrl(const std::string &string) const;
+};
+
+///
+/// LLUrlEntryIcon describes an icon with <icon>...</icon> tags
+///
+class LLUrlEntryIcon : public LLUrlEntryBase
+{
+public:
+ LLUrlEntryIcon();
+ /*virtual*/ std::string getUrl(const std::string &string) const;
+ /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
+ /*virtual*/ std::string getIcon(const std::string &url);
+};
+
+
#endif
diff --git a/indra/llui/llurlmatch.cpp b/indra/llui/llurlmatch.cpp
index 3b47145a22..72a199c220 100644
--- a/indra/llui/llurlmatch.cpp
+++ b/indra/llui/llurlmatch.cpp
@@ -41,14 +41,17 @@ LLUrlMatch::LLUrlMatch() :
mLabel(""),
mTooltip(""),
mIcon(""),
- mMenuName("")
+ mMenuName(""),
+ mLocation(""),
+ mDisabledLink(false)
{
}
void LLUrlMatch::setValues(U32 start, U32 end, const std::string &url,
const std::string &label, const std::string &tooltip,
const std::string &icon, const LLUIColor& color,
- const std::string &menu, const std::string &location)
+ const std::string &menu, const std::string &location,
+ bool disabled_link)
{
mStart = start;
mEnd = end;
@@ -59,4 +62,5 @@ void LLUrlMatch::setValues(U32 start, U32 end, const std::string &url,
mColor = color;
mMenuName = menu;
mLocation = location;
+ mDisabledLink = disabled_link;
}
diff --git a/indra/llui/llurlmatch.h b/indra/llui/llurlmatch.h
index 7f5767923a..e86762548b 100644
--- a/indra/llui/llurlmatch.h
+++ b/indra/llui/llurlmatch.h
@@ -83,11 +83,14 @@ public:
/// return the SL location that this Url describes, or "" if none.
std::string getLocation() const { return mLocation; }
+ /// is this a match for a URL that should not be hyperlinked?
+ bool isLinkDisabled() const { return mDisabledLink; }
+
/// Change the contents of this match object (used by LLUrlRegistry)
void setValues(U32 start, U32 end, const std::string &url, const std::string &label,
const std::string &tooltip, const std::string &icon,
const LLUIColor& color, const std::string &menu,
- const std::string &location);
+ const std::string &location, bool disabled_link);
private:
U32 mStart;
@@ -99,6 +102,7 @@ private:
std::string mMenuName;
std::string mLocation;
LLUIColor mColor;
+ bool mDisabledLink;
};
#endif
diff --git a/indra/llui/llurlregistry.cpp b/indra/llui/llurlregistry.cpp
index ad5c0911f8..4341286eb4 100644
--- a/indra/llui/llurlregistry.cpp
+++ b/indra/llui/llurlregistry.cpp
@@ -44,6 +44,8 @@ void LLUrlRegistryNullCallback(const std::string &url, const std::string &label)
LLUrlRegistry::LLUrlRegistry()
{
// Urls are matched in the order that they were registered
+ registerUrl(new LLUrlEntryNoLink());
+ registerUrl(new LLUrlEntryIcon());
registerUrl(new LLUrlEntrySLURL());
registerUrl(new LLUrlEntryHTTP());
registerUrl(new LLUrlEntryHTTPLabel());
@@ -52,8 +54,10 @@ LLUrlRegistry::LLUrlRegistry()
registerUrl(new LLUrlEntryParcel());
registerUrl(new LLUrlEntryTeleport());
registerUrl(new LLUrlEntryWorldMap());
+ registerUrl(new LLUrlEntryObjectIM());
registerUrl(new LLUrlEntryPlace());
registerUrl(new LLUrlEntryInventory());
+ registerUrl(new LLUrlEntryObjectIM());
//LLUrlEntrySL and LLUrlEntrySLLabel have more common pattern,
//so it should be registered in the end of list
registerUrl(new LLUrlEntrySL());
@@ -131,7 +135,9 @@ static bool stringHasUrl(const std::string &text)
text.find(".com") != std::string::npos ||
text.find(".net") != std::string::npos ||
text.find(".edu") != std::string::npos ||
- text.find(".org") != std::string::npos);
+ text.find(".org") != std::string::npos ||
+ text.find("<nolink>") != std::string::npos ||
+ text.find("<icon") != std::string::npos);
}
bool LLUrlRegistry::findUrl(const std::string &text, LLUrlMatch &match, const LLUrlLabelCallback &cb)
@@ -172,11 +178,12 @@ bool LLUrlRegistry::findUrl(const std::string &text, LLUrlMatch &match, const LL
match.setValues(match_start, match_end,
match_entry->getUrl(url),
match_entry->getLabel(url, cb),
- match_entry->getTooltip(),
- match_entry->getIcon(),
+ match_entry->getTooltip(url),
+ match_entry->getIcon(url),
match_entry->getColor(),
match_entry->getMenuName(),
- match_entry->getLocation(url));
+ match_entry->getLocation(url),
+ match_entry->isLinkDisabled());
return true;
}
@@ -204,9 +211,13 @@ bool LLUrlRegistry::findUrl(const LLWString &text, LLUrlMatch &match, const LLUr
S32 end = start + wurl.size() - 1;
match.setValues(start, end, match.getUrl(),
- match.getLabel(), match.getTooltip(),
- match.getIcon(), match.getColor(),
- match.getMenuName(), match.getLocation());
+ match.getLabel(),
+ match.getTooltip(),
+ match.getIcon(),
+ match.getColor(),
+ match.getMenuName(),
+ match.getLocation(),
+ match.isLinkDisabled());
return true;
}
return false;
diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp
index f1b08c380b..e67f0ec3fc 100644
--- a/indra/llui/llview.cpp
+++ b/indra/llui/llview.cpp
@@ -73,9 +73,9 @@ S32 LLView::sLastBottomXML = S32_MIN;
std::vector<LLViewDrawContext*> LLViewDrawContext::sDrawContextStack;
-#if LL_DEBUG
+//#if LL_DEBUG
BOOL LLView::sIsDrawing = FALSE;
-#endif
+//#endif
// Compiler optimization, generate extern template
template class LLView* LLView::getChild<class LLView>(
@@ -150,6 +150,10 @@ LLView::~LLView()
{
dirtyRect();
//llinfos << "Deleting view " << mName << ":" << (void*) this << llendl;
+ if (LLView::sIsDrawing)
+ {
+ lldebugs << "Deleting view " << mName << " during UI draw() phase" << llendl;
+ }
// llassert(LLView::sIsDrawing == FALSE);
// llassert_always(sDepth == 0); // avoid deleting views while drawing! It can subtly break list iterators
@@ -592,11 +596,6 @@ void LLView::setVisible(BOOL visible)
{
if ( mVisible != visible )
{
- if( !visible && (gFocusMgr.getTopCtrl() == this) )
- {
- gFocusMgr.setTopCtrl( NULL );
- }
-
mVisible = visible;
// notify children of visibility change if root, or part of visible hierarchy
@@ -1322,11 +1321,12 @@ void LLView::drawChildren()
if (viewp->getVisible() && viewp->getRect().isValid())
{
+ // check for bad data
+ llassert_always(viewp->getVisible() == TRUE);
// Only draw views that are within the root view
localRectToScreen(viewp->getRect(),&screenRect);
if ( rootRect.overlaps(screenRect) && LLUI::sDirtyRect.overlaps(screenRect))
{
- glMatrixMode(GL_MODELVIEW);
LLUI::pushMatrix();
{
LLUI::translate((F32)viewp->getRect().mLeft, (F32)viewp->getRect().mBottom, 0.f);
@@ -1350,8 +1350,6 @@ void LLView::drawChildren()
}
--sDepth;
}
-
- gGL.getTexUnit(0)->disable();
}
void LLView::dirtyRect()
@@ -1720,6 +1718,7 @@ LLView* LLView::findChildView(const std::string& name, BOOL recurse) const
for ( child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
{
LLView* childp = *child_it;
+ llassert(childp);
if (childp->getName() == name)
{
return childp;
@@ -1731,6 +1730,7 @@ LLView* LLView::findChildView(const std::string& name, BOOL recurse) const
for ( child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
{
LLView* childp = *child_it;
+ llassert(childp);
LLView* viewp = childp->findChildView(name, recurse);
if ( viewp )
{
diff --git a/indra/llui/llview.h b/indra/llui/llview.h
index c4d7313743..efae00f0e5 100644
--- a/indra/llui/llview.h
+++ b/indra/llui/llview.h
@@ -171,9 +171,9 @@ private:
// widgets in general are not copyable
LLView(const LLView& other) {};
public:
-#if LL_DEBUG
+//#if LL_DEBUG
static BOOL sIsDrawing;
-#endif
+//#endif
enum ESoundFlags
{
SILENT = 0,
diff --git a/indra/llui/llviewborder.cpp b/indra/llui/llviewborder.cpp
index 30717f87de..bd9c43c97f 100644
--- a/indra/llui/llviewborder.cpp
+++ b/indra/llui/llviewborder.cpp
@@ -125,14 +125,6 @@ void LLViewBorder::draw()
llassert( FALSE ); // not implemented
}
}
- else
- if( STYLE_TEXTURE == mStyle )
- {
- if( mTexture )
- {
- drawTextures();
- }
- }
LLView::draw();
}
@@ -255,56 +247,6 @@ void LLViewBorder::drawTwoPixelLines()
gl_line_2d(left+1, bottom+1, right-1, bottom+1);
}
-void LLViewBorder::drawTextures()
-{
- //LLGLSUIDefault gls_ui;
-
- //llassert( FALSE ); // TODO: finish implementing
-
- //gGL.color4fv(UI_VERTEX_COLOR.mV);
-
- //gGL.getTexUnit(0)->bind(mTexture);
- //gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_WRAP);
-
- //drawTextureTrapezoid( 0.f, mBorderWidth, getRect().getWidth(), 0, 0 );
- //drawTextureTrapezoid( 90.f, mBorderWidth, getRect().getHeight(), (F32)getRect().getWidth(),0 );
- //drawTextureTrapezoid( 180.f, mBorderWidth, getRect().getWidth(), (F32)getRect().getWidth(),(F32)getRect().getHeight() );
- //drawTextureTrapezoid( 270.f, mBorderWidth, getRect().getHeight(), 0, (F32)getRect().getHeight() );
-}
-
-
-void LLViewBorder::drawTextureTrapezoid( F32 degrees, S32 width, S32 length, F32 start_x, F32 start_y )
-{
- gGL.pushMatrix();
- {
- gGL.translatef(start_x, start_y, 0.f);
- glRotatef( degrees, 0, 0, 1 );
-
- gGL.begin(LLRender::QUADS);
- {
- // width, width /---------\ length-width, width //
- // / \ //
- // / \ //
- // /---------------\ //
- // 0,0 length, 0 //
-
- gGL.texCoord2f( 0, 0 );
- gGL.vertex2i( 0, 0 );
-
- gGL.texCoord2f( (GLfloat)length, 0 );
- gGL.vertex2i( length, 0 );
-
- gGL.texCoord2f( (GLfloat)(length - width), (GLfloat)width );
- gGL.vertex2i( length - width, width );
-
- gGL.texCoord2f( (GLfloat)width, (GLfloat)width );
- gGL.vertex2i( width, width );
- }
- gGL.end();
- }
- gGL.popMatrix();
-}
-
BOOL LLViewBorder::getBevelFromAttribute(LLXMLNodePtr node, LLViewBorder::EBevel& bevel_style)
{
if (node->hasAttribute("bevel_style"))
diff --git a/indra/llui/llviewborder.h b/indra/llui/llviewborder.h
index 92fd569325..342e84fd93 100644
--- a/indra/llui/llviewborder.h
+++ b/indra/llui/llviewborder.h
@@ -99,8 +99,7 @@ private:
void drawOnePixelLines();
void drawTwoPixelLines();
void drawTextures();
- void drawTextureTrapezoid( F32 degrees, S32 width, S32 length, F32 start_x, F32 start_y );
-
+
EBevel mBevel;
EStyle mStyle;
LLUIColor mHighlightLight;
diff --git a/indra/llui/llviewmodel.h b/indra/llui/llviewmodel.h
index c8a9b52cca..992365d44d 100644
--- a/indra/llui/llviewmodel.h
+++ b/indra/llui/llviewmodel.h
@@ -107,7 +107,8 @@ public:
// New functions
/// Get the stored value in string form
- LLWString getDisplay() const { return mDisplay; }
+ const LLWString& getDisplay() const { return mDisplay; }
+
/**
* Set the display string directly (see LLTextEditor). What the user is
* editing is actually the LLWString value rather than the underlying
diff --git a/indra/llui/tests/llurlentry_test.cpp b/indra/llui/tests/llurlentry_test.cpp
index 80be8fcbf7..cbb303a059 100644
--- a/indra/llui/tests/llurlentry_test.cpp
+++ b/indra/llui/tests/llurlentry_test.cpp
@@ -33,7 +33,7 @@ LLUIColor LLUIColorTable::getColor(const std::string& name, const LLColor4& defa
return LLUIColor();
}
-LLUIColor::LLUIColor() {}
+LLUIColor::LLUIColor() : mColorPtr(NULL) {}
namespace tut
{
@@ -52,9 +52,10 @@ namespace
namespace tut
{
- void testRegex(const std::string &testname, boost::regex regex,
+ void testRegex(const std::string &testname, LLUrlEntryBase &entry,
const char *text, const std::string &expected)
{
+ boost::regex regex = entry.getPattern();
std::string url = "";
boost::cmatch result;
bool found = boost::regex_search(text, result, regex);
@@ -62,7 +63,7 @@ namespace tut
{
S32 start = static_cast<U32>(result[0].first - text);
S32 end = static_cast<U32>(result[0].second - text);
- url = std::string(text+start, end-start);
+ url = entry.getUrl(std::string(text+start, end-start));
}
ensure_equals(testname, url, expected);
}
@@ -74,74 +75,73 @@ namespace tut
// test LLUrlEntryHTTP - standard http Urls
//
LLUrlEntryHTTP url;
- boost::regex r = url.getPattern();
- testRegex("no valid url", r,
+ testRegex("no valid url", url,
"htp://slurl.com/",
"");
- testRegex("simple http (1)", r,
+ testRegex("simple http (1)", url,
"http://slurl.com/",
"http://slurl.com/");
- testRegex("simple http (2)", r,
+ testRegex("simple http (2)", url,
"http://slurl.com",
"http://slurl.com");
- testRegex("simple http (3)", r,
+ testRegex("simple http (3)", url,
"http://slurl.com/about.php",
"http://slurl.com/about.php");
- testRegex("simple https", r,
+ testRegex("simple https", url,
"https://slurl.com/about.php",
"https://slurl.com/about.php");
- testRegex("http in text (1)", r,
+ testRegex("http in text (1)", url,
"XX http://slurl.com/ XX",
"http://slurl.com/");
- testRegex("http in text (2)", r,
+ testRegex("http in text (2)", url,
"XX http://slurl.com/about.php XX",
"http://slurl.com/about.php");
- testRegex("https in text", r,
+ testRegex("https in text", url,
"XX https://slurl.com/about.php XX",
"https://slurl.com/about.php");
- testRegex("two http urls", r,
+ testRegex("two http urls", url,
"XX http://slurl.com/about.php http://secondlife.com/ XX",
"http://slurl.com/about.php");
- testRegex("http url with port and username", r,
+ testRegex("http url with port and username", url,
"XX http://nobody@slurl.com:80/about.php http://secondlife.com/ XX",
"http://nobody@slurl.com:80/about.php");
- testRegex("http url with port, username, and query string", r,
+ testRegex("http url with port, username, and query string", url,
"XX http://nobody@slurl.com:80/about.php?title=hi%20there http://secondlife.com/ XX",
"http://nobody@slurl.com:80/about.php?title=hi%20there");
// note: terminating commas will be removed by LLUrlRegistry:findUrl()
- testRegex("http url with commas in middle and terminating", r,
+ testRegex("http url with commas in middle and terminating", url,
"XX http://slurl.com/?title=Hi,There, XX",
"http://slurl.com/?title=Hi,There,");
// note: terminating periods will be removed by LLUrlRegistry:findUrl()
- testRegex("http url with periods in middle and terminating", r,
+ testRegex("http url with periods in middle and terminating", url,
"XX http://slurl.com/index.php. XX",
"http://slurl.com/index.php.");
// DEV-19842: Closing parenthesis ")" breaks urls
- testRegex("http url with brackets (1)", r,
+ testRegex("http url with brackets (1)", url,
"XX http://en.wikipedia.org/wiki/JIRA_(software) XX",
"http://en.wikipedia.org/wiki/JIRA_(software)");
// DEV-19842: Closing parenthesis ")" breaks urls
- testRegex("http url with brackets (2)", r,
+ testRegex("http url with brackets (2)", url,
"XX http://jira.secondlife.com/secure/attachment/17990/eggy+avs+in+1.21.0+(93713)+public+nightly.jpg XX",
"http://jira.secondlife.com/secure/attachment/17990/eggy+avs+in+1.21.0+(93713)+public+nightly.jpg");
// DEV-10353: URLs in chat log terminated incorrectly when newline in chat
- testRegex("http url with newlines", r,
+ testRegex("http url with newlines", url,
"XX\nhttp://www.secondlife.com/\nXX",
"http://www.secondlife.com/");
}
@@ -153,39 +153,38 @@ namespace tut
// test LLUrlEntryHTTPLabel - wiki-style http Urls with labels
//
LLUrlEntryHTTPLabel url;
- boost::regex r = url.getPattern();
- testRegex("invalid wiki url [1]", r,
+ testRegex("invalid wiki url [1]", url,
"[http://www.example.org]",
"");
- testRegex("invalid wiki url [2]", r,
+ testRegex("invalid wiki url [2]", url,
"[http://www.example.org",
"");
- testRegex("invalid wiki url [3]", r,
+ testRegex("invalid wiki url [3]", url,
"[http://www.example.org Label",
"");
- testRegex("example.org with label (spaces)", r,
+ testRegex("example.org with label (spaces)", url,
"[http://www.example.org Text]",
- "[http://www.example.org Text]");
+ "http://www.example.org");
- testRegex("example.org with label (tabs)", r,
+ testRegex("example.org with label (tabs)", url,
"[http://www.example.org\t Text]",
- "[http://www.example.org\t Text]");
+ "http://www.example.org");
- testRegex("SL http URL with label", r,
+ testRegex("SL http URL with label", url,
"[http://www.secondlife.com/ Second Life]",
- "[http://www.secondlife.com/ Second Life]");
+ "http://www.secondlife.com/");
- testRegex("SL https URL with label", r,
+ testRegex("SL https URL with label", url,
"XXX [https://www.secondlife.com/ Second Life] YYY",
- "[https://www.secondlife.com/ Second Life]");
+ "https://www.secondlife.com/");
- testRegex("SL http URL with label", r,
+ testRegex("SL http URL with label", url,
"[http://www.secondlife.com/?test=Hi%20There Second Life]",
- "[http://www.secondlife.com/?test=Hi%20There Second Life]");
+ "http://www.secondlife.com/?test=Hi%20There");
}
template<> template<>
@@ -195,69 +194,68 @@ namespace tut
// test LLUrlEntrySLURL - second life URLs
//
LLUrlEntrySLURL url;
- boost::regex r = url.getPattern();
- testRegex("no valid slurl [1]", r,
+ testRegex("no valid slurl [1]", url,
"htp://slurl.com/secondlife/Ahern/50/50/50/",
"");
- testRegex("no valid slurl [2]", r,
+ testRegex("no valid slurl [2]", url,
"http://slurl.com/secondlife/",
"");
- testRegex("no valid slurl [3]", r,
+ testRegex("no valid slurl [3]", url,
"hhtp://slurl.com/secondlife/Ahern/50/FOO/50/",
"");
- testRegex("Ahern (50,50,50) [1]", r,
+ testRegex("Ahern (50,50,50) [1]", url,
"http://slurl.com/secondlife/Ahern/50/50/50/",
"http://slurl.com/secondlife/Ahern/50/50/50/");
- testRegex("Ahern (50,50,50) [2]", r,
+ testRegex("Ahern (50,50,50) [2]", url,
"XXX http://slurl.com/secondlife/Ahern/50/50/50/ XXX",
"http://slurl.com/secondlife/Ahern/50/50/50/");
- testRegex("Ahern (50,50,50) [3]", r,
+ testRegex("Ahern (50,50,50) [3]", url,
"XXX http://slurl.com/secondlife/Ahern/50/50/50 XXX",
"http://slurl.com/secondlife/Ahern/50/50/50");
- testRegex("Ahern (50,50,50) multicase", r,
+ testRegex("Ahern (50,50,50) multicase", url,
"XXX http://SLUrl.com/SecondLife/Ahern/50/50/50/ XXX",
"http://SLUrl.com/SecondLife/Ahern/50/50/50/");
- testRegex("Ahern (50,50) [1]", r,
+ testRegex("Ahern (50,50) [1]", url,
"XXX http://slurl.com/secondlife/Ahern/50/50/ XXX",
"http://slurl.com/secondlife/Ahern/50/50/");
- testRegex("Ahern (50,50) [2]", r,
+ testRegex("Ahern (50,50) [2]", url,
"XXX http://slurl.com/secondlife/Ahern/50/50 XXX",
"http://slurl.com/secondlife/Ahern/50/50");
- testRegex("Ahern (50)", r,
+ testRegex("Ahern (50)", url,
"XXX http://slurl.com/secondlife/Ahern/50 XXX",
"http://slurl.com/secondlife/Ahern/50");
- testRegex("Ahern", r,
+ testRegex("Ahern", url,
"XXX http://slurl.com/secondlife/Ahern/ XXX",
"http://slurl.com/secondlife/Ahern/");
- testRegex("Ahern SLURL with title", r,
+ testRegex("Ahern SLURL with title", url,
"XXX http://slurl.com/secondlife/Ahern/50/50/50/?title=YOUR%20TITLE%20HERE! XXX",
"http://slurl.com/secondlife/Ahern/50/50/50/?title=YOUR%20TITLE%20HERE!");
- testRegex("Ahern SLURL with msg", r,
+ testRegex("Ahern SLURL with msg", url,
"XXX http://slurl.com/secondlife/Ahern/50/50/50/?msg=Your%20text%20here. XXX",
"http://slurl.com/secondlife/Ahern/50/50/50/?msg=Your%20text%20here.");
// DEV-21577: In-world SLURLs containing "(" or ")" are not treated as a hyperlink in chat
- testRegex("SLURL with brackets", r,
+ testRegex("SLURL with brackets", url,
"XXX http://slurl.com/secondlife/Burning%20Life%20(Hyper)/27/210/30 XXX",
"http://slurl.com/secondlife/Burning%20Life%20(Hyper)/27/210/30");
// DEV-35459: SLURLs and teleport Links not parsed properly
- testRegex("SLURL with quote", r,
+ testRegex("SLURL with quote", url,
"XXX http://slurl.com/secondlife/A'ksha%20Oasis/41/166/701 XXX",
- "http://slurl.com/secondlife/A'ksha%20Oasis/41/166/701");
+ "http://slurl.com/secondlife/A%27ksha%20Oasis/41/166/701");
}
template<> template<>
@@ -267,25 +265,24 @@ namespace tut
// test LLUrlEntryAgent - secondlife://app/agent Urls
//
LLUrlEntryAgent url;
- boost::regex r = url.getPattern();
- testRegex("Invalid Agent Url", r,
+ testRegex("Invalid Agent Url", url,
"secondlife:///app/agent/0e346d8b-4433-4d66-XXXX-fd37083abc4c/about",
"");
- testRegex("Agent Url ", r,
+ testRegex("Agent Url ", url,
"secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about",
"secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about");
- testRegex("Agent Url in text", r,
+ testRegex("Agent Url in text", url,
"XXX secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about XXX",
"secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about");
- testRegex("Agent Url multicase", r,
+ testRegex("Agent Url multicase", url,
"XXX secondlife:///App/AGENT/0E346D8B-4433-4d66-a6b0-fd37083abc4c/About XXX",
"secondlife:///App/AGENT/0E346D8B-4433-4d66-a6b0-fd37083abc4c/About");
- testRegex("Agent Url alternate command", r,
+ testRegex("Agent Url alternate command", url,
"XXX secondlife:///App/AGENT/0E346D8B-4433-4d66-a6b0-fd37083abc4c/foobar",
"secondlife:///App/AGENT/0E346D8B-4433-4d66-a6b0-fd37083abc4c/foobar");
@@ -298,25 +295,24 @@ namespace tut
// test LLUrlEntryGroup - secondlife://app/group Urls
//
LLUrlEntryGroup url;
- boost::regex r = url.getPattern();
- testRegex("Invalid Group Url", r,
+ testRegex("Invalid Group Url", url,
"secondlife:///app/group/00005ff3-4044-c79f-XXXX-fb28ae0df991/about",
"");
- testRegex("Group Url ", r,
+ testRegex("Group Url ", url,
"secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/about",
"secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/about");
- testRegex("Group Url ", r,
+ testRegex("Group Url ", url,
"secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/inspect",
"secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/inspect");
- testRegex("Group Url in text", r,
+ testRegex("Group Url in text", url,
"XXX secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/about XXX",
"secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/about");
- testRegex("Group Url multicase", r,
+ testRegex("Group Url multicase", url,
"XXX secondlife:///APP/Group/00005FF3-4044-c79f-9de8-fb28ae0df991/About XXX",
"secondlife:///APP/Group/00005FF3-4044-c79f-9de8-fb28ae0df991/About");
}
@@ -328,45 +324,44 @@ namespace tut
// test LLUrlEntryPlace - secondlife://<location> URLs
//
LLUrlEntryPlace url;
- boost::regex r = url.getPattern();
- testRegex("no valid slurl [1]", r,
+ testRegex("no valid slurl [1]", url,
"secondlife://Ahern/FOO/50/",
"");
- testRegex("Ahern (50,50,50) [1]", r,
+ testRegex("Ahern (50,50,50) [1]", url,
"secondlife://Ahern/50/50/50/",
"secondlife://Ahern/50/50/50/");
- testRegex("Ahern (50,50,50) [2]", r,
+ testRegex("Ahern (50,50,50) [2]", url,
"XXX secondlife://Ahern/50/50/50/ XXX",
"secondlife://Ahern/50/50/50/");
- testRegex("Ahern (50,50,50) [3]", r,
+ testRegex("Ahern (50,50,50) [3]", url,
"XXX secondlife://Ahern/50/50/50 XXX",
"secondlife://Ahern/50/50/50");
- testRegex("Ahern (50,50,50) multicase", r,
+ testRegex("Ahern (50,50,50) multicase", url,
"XXX SecondLife://Ahern/50/50/50/ XXX",
"SecondLife://Ahern/50/50/50/");
- testRegex("Ahern (50,50) [1]", r,
+ testRegex("Ahern (50,50) [1]", url,
"XXX secondlife://Ahern/50/50/ XXX",
"secondlife://Ahern/50/50/");
- testRegex("Ahern (50,50) [2]", r,
+ testRegex("Ahern (50,50) [2]", url,
"XXX secondlife://Ahern/50/50 XXX",
"secondlife://Ahern/50/50");
// DEV-21577: In-world SLURLs containing "(" or ")" are not treated as a hyperlink in chat
- testRegex("SLURL with brackets", r,
+ testRegex("SLURL with brackets", url,
"XXX secondlife://Burning%20Life%20(Hyper)/27/210/30 XXX",
"secondlife://Burning%20Life%20(Hyper)/27/210/30");
// DEV-35459: SLURLs and teleport Links not parsed properly
- testRegex("SLURL with quote", r,
+ testRegex("SLURL with quote", url,
"XXX secondlife://A'ksha%20Oasis/41/166/701 XXX",
- "secondlife://A'ksha%20Oasis/41/166/701");
+ "secondlife://A%27ksha%20Oasis/41/166/701");
}
template<> template<>
@@ -376,21 +371,20 @@ namespace tut
// test LLUrlEntryParcel - secondlife://app/parcel Urls
//
LLUrlEntryParcel url;
- boost::regex r = url.getPattern();
- testRegex("Invalid Classified Url", r,
+ testRegex("Invalid Classified Url", url,
"secondlife:///app/parcel/0000060e-4b39-e00b-XXXX-d98b1934e3a8/about",
"");
- testRegex("Classified Url ", r,
+ testRegex("Classified Url ", url,
"secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about",
"secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about");
- testRegex("Classified Url in text", r,
+ testRegex("Classified Url in text", url,
"XXX secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about XXX",
"secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about");
- testRegex("Classified Url multicase", r,
+ testRegex("Classified Url multicase", url,
"XXX secondlife:///APP/Parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/About XXX",
"secondlife:///APP/Parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/About");
}
@@ -401,73 +395,72 @@ namespace tut
// test LLUrlEntryTeleport - secondlife://app/teleport URLs
//
LLUrlEntryTeleport url;
- boost::regex r = url.getPattern();
- testRegex("no valid teleport [1]", r,
+ testRegex("no valid teleport [1]", url,
"http://slurl.com/secondlife/Ahern/50/50/50/",
"");
- testRegex("no valid teleport [2]", r,
+ testRegex("no valid teleport [2]", url,
"secondlife:///app/teleport/",
"");
- testRegex("no valid teleport [3]", r,
+ testRegex("no valid teleport [3]", url,
"second-life:///app/teleport/Ahern/50/50/50/",
"");
- testRegex("no valid teleport [3]", r,
+ testRegex("no valid teleport [3]", url,
"hhtp://slurl.com/secondlife/Ahern/50/FOO/50/",
"");
- testRegex("Ahern (50,50,50) [1]", r,
+ testRegex("Ahern (50,50,50) [1]", url,
"secondlife:///app/teleport/Ahern/50/50/50/",
"secondlife:///app/teleport/Ahern/50/50/50/");
- testRegex("Ahern (50,50,50) [2]", r,
+ testRegex("Ahern (50,50,50) [2]", url,
"XXX secondlife:///app/teleport/Ahern/50/50/50/ XXX",
"secondlife:///app/teleport/Ahern/50/50/50/");
- testRegex("Ahern (50,50,50) [3]", r,
+ testRegex("Ahern (50,50,50) [3]", url,
"XXX secondlife:///app/teleport/Ahern/50/50/50 XXX",
"secondlife:///app/teleport/Ahern/50/50/50");
- testRegex("Ahern (50,50,50) multicase", r,
+ testRegex("Ahern (50,50,50) multicase", url,
"XXX secondlife:///app/teleport/Ahern/50/50/50/ XXX",
"secondlife:///app/teleport/Ahern/50/50/50/");
- testRegex("Ahern (50,50) [1]", r,
+ testRegex("Ahern (50,50) [1]", url,
"XXX secondlife:///app/teleport/Ahern/50/50/ XXX",
"secondlife:///app/teleport/Ahern/50/50/");
- testRegex("Ahern (50,50) [2]", r,
+ testRegex("Ahern (50,50) [2]", url,
"XXX secondlife:///app/teleport/Ahern/50/50 XXX",
"secondlife:///app/teleport/Ahern/50/50");
- testRegex("Ahern (50)", r,
+ testRegex("Ahern (50)", url,
"XXX secondlife:///app/teleport/Ahern/50 XXX",
"secondlife:///app/teleport/Ahern/50");
- testRegex("Ahern", r,
+ testRegex("Ahern", url,
"XXX secondlife:///app/teleport/Ahern/ XXX",
"secondlife:///app/teleport/Ahern/");
- testRegex("Ahern teleport with title", r,
+ testRegex("Ahern teleport with title", url,
"XXX secondlife:///app/teleport/Ahern/50/50/50/?title=YOUR%20TITLE%20HERE! XXX",
"secondlife:///app/teleport/Ahern/50/50/50/?title=YOUR%20TITLE%20HERE!");
- testRegex("Ahern teleport with msg", r,
+ testRegex("Ahern teleport with msg", url,
"XXX secondlife:///app/teleport/Ahern/50/50/50/?msg=Your%20text%20here. XXX",
"secondlife:///app/teleport/Ahern/50/50/50/?msg=Your%20text%20here.");
// DEV-21577: In-world SLURLs containing "(" or ")" are not treated as a hyperlink in chat
- testRegex("Teleport with brackets", r,
+ testRegex("Teleport with brackets", url,
"XXX secondlife:///app/teleport/Burning%20Life%20(Hyper)/27/210/30 XXX",
"secondlife:///app/teleport/Burning%20Life%20(Hyper)/27/210/30");
// DEV-35459: SLURLs and teleport Links not parsed properly
- testRegex("Teleport url with quote", r,
+ testRegex("Teleport url with quote", url,
"XXX secondlife:///app/teleport/A'ksha%20Oasis/41/166/701 XXX",
- "secondlife:///app/teleport/A'ksha%20Oasis/41/166/701");
+ "secondlife:///app/teleport/A%27ksha%20Oasis/41/166/701");
}
template<> template<>
@@ -477,33 +470,32 @@ namespace tut
// test LLUrlEntrySL - general secondlife:// URLs
//
LLUrlEntrySL url;
- boost::regex r = url.getPattern();
- testRegex("no valid slapp [1]", r,
+ testRegex("no valid slapp [1]", url,
"http:///app/",
"");
- testRegex("valid slapp [1]", r,
+ testRegex("valid slapp [1]", url,
"secondlife:///app/",
"secondlife:///app/");
- testRegex("valid slapp [2]", r,
+ testRegex("valid slapp [2]", url,
"secondlife:///app/teleport/Ahern/50/50/50/",
"secondlife:///app/teleport/Ahern/50/50/50/");
- testRegex("valid slapp [3]", r,
+ testRegex("valid slapp [3]", url,
"secondlife:///app/foo",
"secondlife:///app/foo");
- testRegex("valid slapp [4]", r,
+ testRegex("valid slapp [4]", url,
"secondlife:///APP/foo?title=Hi%20There",
"secondlife:///APP/foo?title=Hi%20There");
- testRegex("valid slapp [5]", r,
+ testRegex("valid slapp [5]", url,
"secondlife://host/app/",
"secondlife://host/app/");
- testRegex("valid slapp [6]", r,
+ testRegex("valid slapp [6]", url,
"secondlife://host:8080/foo/bar",
"secondlife://host:8080/foo/bar");
}
@@ -515,35 +507,34 @@ namespace tut
// test LLUrlEntrySLLabel - general secondlife:// URLs with labels
//
LLUrlEntrySLLabel url;
- boost::regex r = url.getPattern();
- testRegex("invalid wiki url [1]", r,
+ testRegex("invalid wiki url [1]", url,
"[secondlife:///app/]",
"");
- testRegex("invalid wiki url [2]", r,
+ testRegex("invalid wiki url [2]", url,
"[secondlife:///app/",
"");
- testRegex("invalid wiki url [3]", r,
+ testRegex("invalid wiki url [3]", url,
"[secondlife:///app/ Label",
"");
- testRegex("agent slurl with label (spaces)", r,
+ testRegex("agent slurl with label (spaces)", url,
"[secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about Text]",
- "[secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about Text]");
+ "secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about");
- testRegex("agent slurl with label (tabs)", r,
+ testRegex("agent slurl with label (tabs)", url,
"[secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about\t Text]",
- "[secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about\t Text]");
+ "secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about");
- testRegex("agent slurl with label", r,
+ testRegex("agent slurl with label", url,
"[secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about FirstName LastName]",
- "[secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about FirstName LastName]");
+ "secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about");
- testRegex("teleport slurl with label", r,
+ testRegex("teleport slurl with label", url,
"XXX [secondlife:///app/teleport/Ahern/50/50/50/ Teleport to Ahern] YYY",
- "[secondlife:///app/teleport/Ahern/50/50/50/ Teleport to Ahern]");
+ "secondlife:///app/teleport/Ahern/50/50/50/");
}
template<> template<>
@@ -553,62 +544,98 @@ namespace tut
// test LLUrlEntryHTTPNoProtocol - general URLs without a protocol
//
LLUrlEntryHTTPNoProtocol url;
- boost::regex r = url.getPattern();
- testRegex("naked .com URL", r,
+ testRegex("naked .com URL", url,
"see google.com",
- "google.com");
+ "http://google.com");
- testRegex("naked .org URL", r,
+ testRegex("naked .org URL", url,
"see en.wikipedia.org for details",
- "en.wikipedia.org");
+ "http://en.wikipedia.org");
- testRegex("naked .net URL", r,
+ testRegex("naked .net URL", url,
"example.net",
- "example.net");
+ "http://example.net");
- testRegex("naked .edu URL (2 instances)", r,
+ testRegex("naked .edu URL (2 instances)", url,
"MIT web site is at web.mit.edu and also www.mit.edu",
- "web.mit.edu");
+ "http://web.mit.edu");
- testRegex("don't match e-mail addresses", r,
+ testRegex("don't match e-mail addresses", url,
"test@lindenlab.com",
"");
- testRegex(".com URL with path", r,
+ testRegex(".com URL with path", url,
"see secondlife.com/status for grid status",
- "secondlife.com/status");
+ "http://secondlife.com/status");
- testRegex(".com URL with port", r,
+ testRegex(".com URL with port", url,
"secondlife.com:80",
- "secondlife.com:80");
+ "http://secondlife.com:80");
- testRegex(".com URL with port and path", r,
+ testRegex(".com URL with port and path", url,
"see secondlife.com:80/status",
- "secondlife.com:80/status");
+ "http://secondlife.com:80/status");
- testRegex("www.*.com URL with port and path", r,
+ testRegex("www.*.com URL with port and path", url,
"see www.secondlife.com:80/status",
- "www.secondlife.com:80/status");
+ "http://www.secondlife.com:80/status");
- testRegex("invalid .com URL [1]", r,
+ testRegex("invalid .com URL [1]", url,
"..com",
"");
- testRegex("invalid .com URL [2]", r,
+ testRegex("invalid .com URL [2]", url,
"you.come",
"");
- testRegex("invalid .com URL [3]", r,
+ testRegex("invalid .com URL [3]", url,
"recommended",
"");
- testRegex("invalid .edu URL", r,
+ testRegex("invalid .edu URL", url,
"hi there scheduled maitenance has begun",
"");
- testRegex("invalid .net URL", r,
+ testRegex("invalid .net URL", url,
"foo.netty",
"");
+
+ testRegex("XML tags around URL [1]", url,
+ "<foo>secondlife.com</foo>",
+ "http://secondlife.com");
+
+ testRegex("XML tags around URL [2]", url,
+ "<foo>secondlife.com/status?bar=1</foo>",
+ "http://secondlife.com/status?bar=1");
+ }
+
+ template<> template<>
+ void object::test<12>()
+ {
+ //
+ // test LLUrlEntryNoLink - turn off hyperlinking
+ //
+ LLUrlEntryNoLink url;
+
+ testRegex("<nolink> [1]", url,
+ "<nolink>google.com</nolink>",
+ "google.com");
+
+ testRegex("<nolink> [2]", url,
+ "<nolink>google.com",
+ "");
+
+ testRegex("<nolink> [3]", url,
+ "google.com</nolink>",
+ "");
+
+ testRegex("<nolink> [4]", url,
+ "<nolink>Hello World</nolink>",
+ "Hello World");
+
+ testRegex("<nolink> [5]", url,
+ "<nolink>My Object</nolink>",
+ "My Object");
}
}
diff --git a/indra/llui/tests/llurlmatch_test.cpp b/indra/llui/tests/llurlmatch_test.cpp
index e8cf135346..24a32de268 100644
--- a/indra/llui/tests/llurlmatch_test.cpp
+++ b/indra/llui/tests/llurlmatch_test.cpp
@@ -25,6 +25,7 @@
// link seam
LLUIColor::LLUIColor()
+ : mColorPtr(NULL)
{}
namespace tut
@@ -53,7 +54,7 @@ namespace tut
LLUrlMatch match;
ensure("empty()", match.empty());
- match.setValues(0, 1, "http://secondlife.com", "Second Life", "", "", LLUIColor(), "", "");
+ match.setValues(0, 1, "http://secondlife.com", "Second Life", "", "", LLUIColor(), "", "", false);
ensure("! empty()", ! match.empty());
}
@@ -66,7 +67,7 @@ namespace tut
LLUrlMatch match;
ensure_equals("getStart() == 0", match.getStart(), 0);
- match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "");
+ match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false);
ensure_equals("getStart() == 10", match.getStart(), 10);
}
@@ -79,7 +80,7 @@ namespace tut
LLUrlMatch match;
ensure_equals("getEnd() == 0", match.getEnd(), 0);
- match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "");
+ match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false);
ensure_equals("getEnd() == 20", match.getEnd(), 20);
}
@@ -92,10 +93,10 @@ namespace tut
LLUrlMatch match;
ensure_equals("getUrl() == ''", match.getUrl(), "");
- match.setValues(10, 20, "http://slurl.com/", "", "", "", LLUIColor(), "", "");
+ match.setValues(10, 20, "http://slurl.com/", "", "", "", LLUIColor(), "", "", false);
ensure_equals("getUrl() == 'http://slurl.com/'", match.getUrl(), "http://slurl.com/");
- match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "");
+ match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false);
ensure_equals("getUrl() == '' (2)", match.getUrl(), "");
}
@@ -108,10 +109,10 @@ namespace tut
LLUrlMatch match;
ensure_equals("getLabel() == ''", match.getLabel(), "");
- match.setValues(10, 20, "", "Label", "", "", LLUIColor(), "", "");
+ match.setValues(10, 20, "", "Label", "", "", LLUIColor(), "", "", false);
ensure_equals("getLabel() == 'Label'", match.getLabel(), "Label");
- match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "");
+ match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false);
ensure_equals("getLabel() == '' (2)", match.getLabel(), "");
}
@@ -124,10 +125,10 @@ namespace tut
LLUrlMatch match;
ensure_equals("getTooltip() == ''", match.getTooltip(), "");
- match.setValues(10, 20, "", "", "Info", "", LLUIColor(), "", "");
+ match.setValues(10, 20, "", "", "Info", "", LLUIColor(), "", "", false);
ensure_equals("getTooltip() == 'Info'", match.getTooltip(), "Info");
- match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "");
+ match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false);
ensure_equals("getTooltip() == '' (2)", match.getTooltip(), "");
}
@@ -140,10 +141,10 @@ namespace tut
LLUrlMatch match;
ensure_equals("getIcon() == ''", match.getIcon(), "");
- match.setValues(10, 20, "", "", "", "Icon", LLUIColor(), "", "");
+ match.setValues(10, 20, "", "", "", "Icon", LLUIColor(), "", "", false);
ensure_equals("getIcon() == 'Icon'", match.getIcon(), "Icon");
- match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "");
+ match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false);
ensure_equals("getIcon() == '' (2)", match.getIcon(), "");
}
@@ -156,10 +157,10 @@ namespace tut
LLUrlMatch match;
ensure("getMenuName() empty", match.getMenuName().empty());
- match.setValues(10, 20, "", "", "", "Icon", LLUIColor(), "xui_file.xml", "");
+ match.setValues(10, 20, "", "", "", "Icon", LLUIColor(), "xui_file.xml", "", false);
ensure_equals("getMenuName() == \"xui_file.xml\"", match.getMenuName(), "xui_file.xml");
- match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "");
+ match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false);
ensure("getMenuName() empty (2)", match.getMenuName().empty());
}
@@ -172,10 +173,10 @@ namespace tut
LLUrlMatch match;
ensure("getLocation() empty", match.getLocation().empty());
- match.setValues(10, 20, "", "", "", "Icon", LLUIColor(), "xui_file.xml", "Paris");
+ match.setValues(10, 20, "", "", "", "Icon", LLUIColor(), "xui_file.xml", "Paris", false);
ensure_equals("getLocation() == \"Paris\"", match.getLocation(), "Paris");
- match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "");
+ match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false);
ensure("getLocation() empty (2)", match.getLocation().empty());
}
}