summaryrefslogtreecommitdiff
path: root/indra/llui
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llui')
-rw-r--r--indra/llui/CMakeLists.txt99
-rw-r--r--indra/llui/llaccordionctrl.cpp914
-rw-r--r--indra/llui/llaccordionctrl.h193
-rw-r--r--indra/llui/llaccordionctrltab.cpp1065
-rw-r--r--indra/llui/llaccordionctrltab.h247
-rw-r--r--indra/llui/llbutton.cpp649
-rw-r--r--indra/llui/llbutton.h256
-rw-r--r--indra/llui/llcallbackmap.h39
-rw-r--r--indra/llui/llcheckboxctrl.cpp80
-rw-r--r--indra/llui/llcheckboxctrl.h49
-rw-r--r--indra/llui/llclipboard.cpp42
-rw-r--r--indra/llui/llclipboard.h37
-rw-r--r--indra/llui/llcombobox.cpp276
-rw-r--r--indra/llui/llcombobox.h95
-rw-r--r--indra/llui/llconsole.cpp124
-rw-r--r--indra/llui/llconsole.h57
-rw-r--r--indra/llui/llcontainerview.cpp65
-rw-r--r--indra/llui/llcontainerview.h44
-rw-r--r--indra/llui/llctrlselectioninterface.cpp36
-rw-r--r--indra/llui/llctrlselectioninterface.h36
-rw-r--r--indra/llui/lldockablefloater.cpp245
-rw-r--r--indra/llui/lldockablefloater.h149
-rw-r--r--indra/llui/lldockcontrol.cpp342
-rw-r--r--indra/llui/lldockcontrol.h94
-rw-r--r--indra/llui/lldraghandle.cpp78
-rw-r--r--indra/llui/lldraghandle.h50
-rw-r--r--indra/llui/lleditmenuhandler.cpp43
-rw-r--r--indra/llui/lleditmenuhandler.h38
-rw-r--r--indra/llui/llf32uictrl.cpp36
-rw-r--r--indra/llui/llf32uictrl.h36
-rw-r--r--indra/llui/llfiltereditor.cpp46
-rw-r--r--indra/llui/llfiltereditor.h59
-rw-r--r--indra/llui/llflatlistview.cpp1342
-rw-r--r--indra/llui/llflatlistview.h519
-rw-r--r--indra/llui/llfloater.cpp1207
-rw-r--r--indra/llui/llfloater.h297
-rw-r--r--indra/llui/llfloaterreg.cpp136
-rw-r--r--indra/llui/llfloaterreg.h50
-rw-r--r--indra/llui/llfloaterreglistener.cpp147
-rw-r--r--indra/llui/llfloaterreglistener.h53
-rw-r--r--indra/llui/llflyoutbutton.cpp53
-rw-r--r--indra/llui/llflyoutbutton.h45
-rw-r--r--indra/llui/llfocusmgr.cpp256
-rw-r--r--indra/llui/llfocusmgr.h106
-rw-r--r--indra/llui/llfunctorregistry.cpp37
-rw-r--r--indra/llui/llfunctorregistry.h36
-rw-r--r--indra/llui/llhandle.h172
-rw-r--r--indra/llui/llhelp.h43
-rw-r--r--indra/llui/lliconctrl.cpp63
-rw-r--r--indra/llui/lliconctrl.h53
-rw-r--r--indra/llui/llkeywords.cpp187
-rw-r--r--indra/llui/llkeywords.h73
-rw-r--r--indra/llui/lllayoutstack.cpp214
-rw-r--r--indra/llui/lllayoutstack.h86
-rw-r--r--indra/llui/lllazyvalue.h36
-rw-r--r--indra/llui/lllineeditor.cpp803
-rw-r--r--indra/llui/lllineeditor.h161
-rw-r--r--indra/llui/llloadingindicator.cpp128
-rw-r--r--indra/llui/llloadingindicator.h87
-rw-r--r--indra/llui/lllocalcliprect.cpp110
-rw-r--r--indra/llui/lllocalcliprect.h63
-rw-r--r--indra/llui/llmenubutton.cpp137
-rw-r--r--indra/llui/llmenubutton.h64
-rw-r--r--indra/llui/llmenugl.cpp943
-rw-r--r--indra/llui/llmenugl.h196
-rw-r--r--indra/llui/llmodaldialog.cpp94
-rw-r--r--indra/llui/llmodaldialog.h55
-rw-r--r--indra/llui/llmultifloater.cpp120
-rw-r--r--indra/llui/llmultifloater.h40
-rw-r--r--indra/llui/llmultislider.cpp124
-rw-r--r--indra/llui/llmultislider.h79
-rw-r--r--indra/llui/llmultisliderctrl.cpp68
-rw-r--r--indra/llui/llmultisliderctrl.h50
-rw-r--r--indra/llui/llnotificationptr.h35
-rw-r--r--indra/llui/llnotifications.cpp338
-rw-r--r--indra/llui/llnotifications.h227
-rw-r--r--indra/llui/llnotificationslistener.cpp349
-rw-r--r--indra/llui/llnotificationslistener.h69
-rw-r--r--indra/llui/llnotificationsutil.cpp95
-rw-r--r--indra/llui/llnotificationsutil.h66
-rw-r--r--indra/llui/llpanel.cpp412
-rw-r--r--indra/llui/llpanel.h169
-rw-r--r--indra/llui/llprogressbar.cpp58
-rw-r--r--indra/llui/llprogressbar.h44
-rw-r--r--indra/llui/llradiogroup.cpp216
-rw-r--r--indra/llui/llradiogroup.h92
-rw-r--r--indra/llui/llresizebar.cpp48
-rw-r--r--indra/llui/llresizebar.h42
-rw-r--r--indra/llui/llresizehandle.cpp49
-rw-r--r--indra/llui/llresizehandle.h41
-rw-r--r--indra/llui/llresmgr.cpp48
-rw-r--r--indra/llui/llresmgr.h36
-rw-r--r--indra/llui/llrngwriter.cpp310
-rw-r--r--indra/llui/llrngwriter.h63
-rw-r--r--indra/llui/llscrollbar.cpp172
-rw-r--r--indra/llui/llscrollbar.h76
-rw-r--r--indra/llui/llscrollcontainer.cpp434
-rw-r--r--indra/llui/llscrollcontainer.h75
-rw-r--r--indra/llui/llscrollingpanellist.cpp93
-rw-r--r--indra/llui/llscrollingpanellist.h47
-rw-r--r--indra/llui/llscrolllistcell.cpp96
-rw-r--r--indra/llui/llscrolllistcell.h63
-rw-r--r--indra/llui/llscrolllistcolumn.cpp58
-rw-r--r--indra/llui/llscrolllistcolumn.h67
-rw-r--r--indra/llui/llscrolllistctrl.cpp404
-rw-r--r--indra/llui/llscrolllistctrl.h146
-rw-r--r--indra/llui/llscrolllistitem.cpp37
-rw-r--r--indra/llui/llscrolllistitem.h56
-rw-r--r--indra/llui/llsdparam.cpp46
-rw-r--r--indra/llui/llsdparam.h36
-rw-r--r--indra/llui/llsearcheditor.cpp181
-rw-r--r--indra/llui/llsearcheditor.h86
-rw-r--r--indra/llui/llslider.cpp253
-rw-r--r--indra/llui/llslider.h77
-rw-r--r--indra/llui/llsliderctrl.cpp70
-rw-r--r--indra/llui/llsliderctrl.h53
-rw-r--r--indra/llui/llspinctrl.cpp183
-rw-r--r--indra/llui/llspinctrl.h45
-rw-r--r--indra/llui/llstatbar.cpp36
-rw-r--r--indra/llui/llstatbar.h36
-rw-r--r--indra/llui/llstatgraph.cpp40
-rw-r--r--indra/llui/llstatgraph.h36
-rw-r--r--indra/llui/llstatview.cpp49
-rw-r--r--indra/llui/llstatview.h46
-rw-r--r--indra/llui/llstyle.cpp177
-rw-r--r--indra/llui/llstyle.h136
-rw-r--r--indra/llui/lltabcontainer.cpp595
-rw-r--r--indra/llui/lltabcontainer.h124
-rw-r--r--indra/llui/lltextbase.cpp2901
-rw-r--r--indra/llui/lltextbase.h569
-rw-r--r--indra/llui/lltextbox.cpp413
-rw-r--r--indra/llui/lltextbox.h159
-rw-r--r--indra/llui/lltexteditor.cpp3022
-rw-r--r--indra/llui/lltexteditor.h491
-rw-r--r--indra/llui/lltextparser.cpp80
-rw-r--r--indra/llui/lltextparser.h63
-rw-r--r--indra/llui/lltextutil.cpp107
-rw-r--r--indra/llui/lltextutil.h81
-rw-r--r--indra/llui/lltextvalidate.cpp314
-rw-r--r--indra/llui/lltextvalidate.h58
-rw-r--r--indra/llui/lltoggleablemenu.cpp83
-rw-r--r--indra/llui/lltoggleablemenu.h62
-rw-r--r--indra/llui/lltooltip.cpp589
-rw-r--r--indra/llui/lltooltip.h166
-rw-r--r--indra/llui/lltransutil.cpp61
-rw-r--r--indra/llui/lltransutil.h45
-rw-r--r--indra/llui/llui.cpp958
-rw-r--r--indra/llui/llui.h535
-rw-r--r--indra/llui/lluicolortable.cpp212
-rw-r--r--indra/llui/lluicolortable.h63
-rw-r--r--indra/llui/lluiconstants.h36
-rw-r--r--indra/llui/lluictrl.cpp522
-rw-r--r--indra/llui/lluictrl.h237
-rw-r--r--indra/llui/lluictrlfactory.cpp849
-rw-r--r--indra/llui/lluictrlfactory.h341
-rw-r--r--indra/llui/lluifwd.h37
-rw-r--r--indra/llui/lluiimage.cpp105
-rw-r--r--indra/llui/lluiimage.h77
-rw-r--r--indra/llui/lluistring.cpp106
-rw-r--r--indra/llui/lluistring.h76
-rw-r--r--indra/llui/llundo.cpp36
-rw-r--r--indra/llui/llundo.h36
-rw-r--r--indra/llui/llurlaction.cpp159
-rw-r--r--indra/llui/llurlaction.h93
-rw-r--r--indra/llui/llurlentry.cpp838
-rw-r--r--indra/llui/llurlentry.h315
-rw-r--r--indra/llui/llurlmatch.cpp63
-rw-r--r--indra/llui/llurlmatch.h111
-rw-r--r--indra/llui/llurlregistry.cpp254
-rw-r--r--indra/llui/llurlregistry.h93
-rw-r--r--indra/llui/llview.cpp1065
-rw-r--r--indra/llui/llview.h362
-rw-r--r--indra/llui/llviewborder.cpp104
-rw-r--r--indra/llui/llviewborder.h41
-rw-r--r--indra/llui/llviewmodel.cpp36
-rw-r--r--indra/llui/llviewmodel.h39
-rw-r--r--indra/llui/llviewquery.cpp50
-rw-r--r--indra/llui/llviewquery.h38
-rw-r--r--indra/llui/tests/llurlentry_stub.cpp69
-rw-r--r--indra/llui/tests/llurlentry_test.cpp670
-rw-r--r--indra/llui/tests/llurlmatch_test.cpp187
181 files changed, 26469 insertions, 12696 deletions
diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt
index 117e8e28ab..8e78a5fefd 100644
--- a/indra/llui/CMakeLists.txt
+++ b/indra/llui/CMakeLists.txt
@@ -3,7 +3,6 @@
project(llui)
include(00-Common)
-include(LLAudio)
include(LLCommon)
include(LLImage)
include(LLMath)
@@ -12,9 +11,9 @@ include(LLRender)
include(LLWindow)
include(LLVFS)
include(LLXML)
+include(LLXUIXML)
include_directories(
- ${LLAUDIO_INCLUDE_DIRS}
${LLCOMMON_INCLUDE_DIRS}
${LLIMAGE_INCLUDE_DIRS}
${LLMATH_INCLUDE_DIRS}
@@ -23,10 +22,12 @@ include_directories(
${LLWINDOW_INCLUDE_DIRS}
${LLVFS_INCLUDE_DIRS}
${LLXML_INCLUDE_DIRS}
+ ${LLXUIXML_INCLUDE_DIRS}
)
set(llui_SOURCE_FILES
- llalertdialog.cpp
+ llaccordionctrl.cpp
+ llaccordionctrltab.cpp
llbutton.cpp
llcheckboxctrl.cpp
llclipboard.cpp
@@ -34,31 +35,41 @@ set(llui_SOURCE_FILES
llconsole.cpp
llcontainerview.cpp
llctrlselectioninterface.cpp
+ lldockablefloater.cpp
+ lldockcontrol.cpp
lldraghandle.cpp
lleditmenuhandler.cpp
llf32uictrl.cpp
+ llfiltereditor.cpp
+ llflatlistview.cpp
llfloater.cpp
llfloaterreg.cpp
+ llfloaterreglistener.cpp
llflyoutbutton.cpp
llfocusmgr.cpp
llfunctorregistry.cpp
lliconctrl.cpp
- llinitparam.cpp
llkeywords.cpp
lllayoutstack.cpp
lllineeditor.cpp
+ llloadingindicator.cpp
+ lllocalcliprect.cpp
+ llmenubutton.cpp
llmenugl.cpp
llmodaldialog.cpp
llmultifloater.cpp
llmultislider.cpp
llmultisliderctrl.cpp
llnotifications.cpp
+ llnotificationslistener.cpp
+ llnotificationsutil.cpp
llpanel.cpp
llprogressbar.cpp
llradiogroup.cpp
llresizebar.cpp
llresizehandle.cpp
llresmgr.cpp
+ llrngwriter.cpp
llscrollbar.cpp
llscrollcontainer.cpp
llscrollingpanellist.cpp
@@ -67,7 +78,7 @@ set(llui_SOURCE_FILES
llscrolllistctrl.cpp
llscrolllistitem.cpp
llsdparam.cpp
- llsearcheditor.cpp
+ llsearcheditor.cpp
llslider.cpp
llsliderctrl.cpp
llspinctrl.cpp
@@ -76,10 +87,15 @@ set(llui_SOURCE_FILES
llstatview.cpp
llstyle.cpp
lltabcontainer.cpp
+ lltextbase.cpp
lltextbox.cpp
lltexteditor.cpp
lltextparser.cpp
- lltrans.cpp
+ lltextutil.cpp
+ lltextvalidate.cpp
+ lltransutil.cpp
+ lltoggleablemenu.cpp
+ lltooltip.cpp
llui.cpp
lluicolortable.cpp
lluictrl.cpp
@@ -87,6 +103,10 @@ set(llui_SOURCE_FILES
lluiimage.cpp
lluistring.cpp
llundo.cpp
+ llurlaction.cpp
+ llurlentry.cpp
+ llurlmatch.cpp
+ llurlregistry.cpp
llviewborder.cpp
llviewmodel.cpp
llview.cpp
@@ -96,7 +116,8 @@ set(llui_SOURCE_FILES
set(llui_HEADER_FILES
CMakeLists.txt
- llalertdialog.h
+ llaccordionctrl.h
+ llaccordionctrltab.h
llbutton.h
llcallbackmap.h
llcheckboxctrl.h
@@ -106,33 +127,44 @@ set(llui_HEADER_FILES
llcontainerview.h
llctrlselectioninterface.h
lldraghandle.h
+ lldockablefloater.h
+ lldockcontrol.h
lleditmenuhandler.h
llf32uictrl.h
+ llfiltereditor.h
+ llflatlistview.h
llfloater.h
llfloaterreg.h
+ llfloaterreglistener.h
llflyoutbutton.h
llfocusmgr.h
llfunctorregistry.h
- llhtmlhelp.h
+ llhandle.h
+ llhelp.h
lliconctrl.h
- llinitparam.h
llkeywords.h
lllayoutstack.h
lllazyvalue.h
lllineeditor.h
+ llloadingindicator.h
+ lllocalcliprect.h
+ llmenubutton.h
llmenugl.h
llmodaldialog.h
llmultifloater.h
llmultisliderctrl.h
llmultislider.h
+ llnotificationptr.h
llnotifications.h
+ llnotificationslistener.h
+ llnotificationsutil.h
llpanel.h
llprogressbar.h
llradiogroup.h
- llregistry.h
llresizebar.h
llresizehandle.h
llresmgr.h
+ llrngwriter.h
llsearcheditor.h
llscrollbar.h
llscrollcontainer.h
@@ -150,10 +182,15 @@ set(llui_HEADER_FILES
llstatview.h
llstyle.h
lltabcontainer.h
+ lltextbase.h
lltextbox.h
lltexteditor.h
lltextparser.h
- lltrans.h
+ lltextutil.h
+ lltextvalidate.h
+ lltoggleablemenu.h
+ lltooltip.h
+ lltransutil.h
lluicolortable.h
lluiconstants.h
lluictrlfactory.h
@@ -163,6 +200,10 @@ set(llui_HEADER_FILES
lluiimage.h
lluistring.h
llundo.h
+ llurlaction.h
+ llurlentry.h
+ llurlmatch.h
+ llurlregistry.h
llviewborder.h
llviewmodel.h
llview.h
@@ -172,17 +213,39 @@ 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})
# Libraries on which this library depends, needed for Linux builds
# Sort by high-level to low-level
target_link_libraries(llui
- llrender
- llwindow
- llimage
- llvfs # ugh, just for LLDir
- llxml
- llcommon # must be after llimage, llwindow, llrender
- llmath
+ ${LLMESSAGE_LIBRARIES}
+ ${LLRENDER_LIBRARIES}
+ ${LLWINDOW_LIBRARIES}
+ ${LLIMAGE_LIBRARIES}
+ ${LLVFS_LIBRARIES} # ugh, just for LLDir
+ ${LLXUIXML_LIBRARIES}
+ ${LLXML_LIBRARIES}
+ ${LLMATH_LIBRARIES}
+ ${LLCOMMON_LIBRARIES} # must be after llimage, llwindow, llrender
)
+
+if(LL_TESTS)
+ # Add tests
+ include(LLAddBuildTest)
+ SET(llui_TEST_SOURCE_FILES
+ llurlmatch.cpp
+ llurlentry.cpp
+ )
+ LL_ADD_PROJECT_UNIT_TESTS(llui "${llui_TEST_SOURCE_FILES}")
+endif(LL_TESTS)
diff --git a/indra/llui/llaccordionctrl.cpp b/indra/llui/llaccordionctrl.cpp
new file mode 100644
index 0000000000..f9ffaaa646
--- /dev/null
+++ b/indra/llui/llaccordionctrl.cpp
@@ -0,0 +1,914 @@
+/**
+ * @file llaccordionctrl.cpp
+ * @brief Accordion panel implementation
+ *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+#include "linden_common.h"
+
+#include "llaccordionctrl.h"
+#include "llaccordionctrltab.h"
+
+#include "lluictrlfactory.h" // builds floaters from XML
+
+#include "llwindow.h"
+#include "llfocusmgr.h"
+#include "lllocalcliprect.h"
+
+#include "boost/bind.hpp"
+
+static const S32 DRAGGER_BAR_MARGIN = 4;
+static const S32 DRAGGER_BAR_HEIGHT = 5;
+static const S32 BORDER_MARGIN = 2;
+static const S32 PARENT_BORDER_MARGIN = 5;
+
+static const S32 panel_delta = DRAGGER_BAR_MARGIN; // Distanse between two panels
+
+static const S32 HORIZONTAL_MULTIPLE = 8;
+static const S32 VERTICAL_MULTIPLE = 16;
+static const F32 MIN_AUTO_SCROLL_RATE = 120.f;
+static const F32 MAX_AUTO_SCROLL_RATE = 500.f;
+static const F32 AUTO_SCROLL_RATE_ACCEL = 120.f;
+
+
+// LLAccordionCtrl =================================================================|
+
+static LLDefaultChildRegistry::Register<LLAccordionCtrl> t2("accordion");
+
+
+LLAccordionCtrl::LLAccordionCtrl(const Params& params):LLPanel(params)
+ , mFitParent(params.fit_parent)
+ , mAutoScrolling( false )
+ , mAutoScrollRate( 0.f )
+ , mSelectedTab( NULL )
+ , mTabComparator( NULL )
+ , mNoVisibleTabsHelpText(NULL)
+ , mNoVisibleTabsOrigString(params.no_visible_tabs_text.initial_value().asString())
+{
+ initNoTabsWidget(params.no_matched_tabs_text);
+
+ mSingleExpansion = params.single_expansion;
+ if(mFitParent && !mSingleExpansion)
+ {
+ llinfos << "fit_parent works best when combined with single_expansion" << llendl;
+ }
+}
+
+LLAccordionCtrl::LLAccordionCtrl() : LLPanel()
+ , mAutoScrolling( false )
+ , mAutoScrollRate( 0.f )
+ , mSelectedTab( NULL )
+ , mNoVisibleTabsHelpText(NULL)
+{
+ initNoTabsWidget(LLTextBox::Params());
+
+ mSingleExpansion = false;
+ mFitParent = false;
+ LLUICtrlFactory::getInstance()->buildPanel(this, "accordion_parent.xml");
+}
+
+//---------------------------------------------------------------------------------
+void LLAccordionCtrl::draw()
+{
+ if (mAutoScrolling)
+ {
+ // add acceleration to autoscroll
+ mAutoScrollRate = llmin(mAutoScrollRate + (LLFrameTimer::getFrameDeltaTimeF32() * AUTO_SCROLL_RATE_ACCEL), MAX_AUTO_SCROLL_RATE);
+ }
+ else
+ {
+ // reset to minimum for next time
+ mAutoScrollRate = MIN_AUTO_SCROLL_RATE;
+ }
+ // clear this flag to be set on next call to autoScroll
+ mAutoScrolling = false;
+
+ LLRect local_rect(0, getRect().getHeight(), getRect().getWidth(), 0);
+
+ LLLocalClipRect clip(local_rect);
+
+ LLPanel::draw();
+}
+
+
+//---------------------------------------------------------------------------------
+BOOL LLAccordionCtrl::postBuild()
+{
+ static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
+
+ LLRect scroll_rect;
+ scroll_rect.setOriginAndSize(
+ getRect().getWidth() - scrollbar_size,
+ 1,
+ scrollbar_size,
+ getRect().getHeight() - 1);
+
+ LLScrollbar::Params sbparams;
+ sbparams.name("scrollable vertical");
+ sbparams.rect(scroll_rect);
+ sbparams.orientation(LLScrollbar::VERTICAL);
+ sbparams.doc_size(mInnerRect.getHeight());
+ sbparams.doc_pos(0);
+ sbparams.page_size(mInnerRect.getHeight());
+ sbparams.step_size(VERTICAL_MULTIPLE);
+ sbparams.follows.flags(FOLLOWS_RIGHT | FOLLOWS_TOP | FOLLOWS_BOTTOM);
+ sbparams.change_callback(boost::bind(&LLAccordionCtrl::onScrollPosChangeCallback, this, _1, _2));
+
+ mScrollbar = LLUICtrlFactory::create<LLScrollbar> (sbparams);
+ LLView::addChild( mScrollbar );
+ mScrollbar->setVisible( false );
+ mScrollbar->setFollowsRight();
+ mScrollbar->setFollowsTop();
+ mScrollbar->setFollowsBottom();
+
+ //if it was created from xml...
+ std::vector<LLUICtrl*> accordion_tabs;
+ for(child_list_const_iter_t it = getChildList()->begin();
+ getChildList()->end() != it; ++it)
+ {
+ LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(*it);
+ if(accordion_tab == NULL)
+ continue;
+ if(std::find(mAccordionTabs.begin(),mAccordionTabs.end(),accordion_tab) == mAccordionTabs.end())
+ {
+ accordion_tabs.push_back(accordion_tab);
+ }
+ }
+
+ for(std::vector<LLUICtrl*>::reverse_iterator it = accordion_tabs.rbegin();it!=accordion_tabs.rend();++it)
+ addCollapsibleCtrl(*it);
+
+ arrange ();
+
+ if(mSingleExpansion)
+ {
+ if(!mAccordionTabs[0]->getDisplayChildren())
+ mAccordionTabs[0]->setDisplayChildren(true);
+ for(size_t i=1;i<mAccordionTabs.size();++i)
+ {
+ if(mAccordionTabs[i]->getDisplayChildren())
+ mAccordionTabs[i]->setDisplayChildren(false);
+ }
+ }
+
+ updateNoTabsHelpTextVisibility();
+
+ return TRUE;
+}
+
+
+//---------------------------------------------------------------------------------
+LLAccordionCtrl::~LLAccordionCtrl()
+{
+ mAccordionTabs.clear();
+}
+
+//---------------------------------------------------------------------------------
+
+void LLAccordionCtrl::reshape(S32 width, S32 height, BOOL called_from_parent)
+{
+ // adjust our rectangle
+ LLRect rcLocal = getRect();
+ rcLocal.mRight = rcLocal.mLeft + width;
+ rcLocal.mTop = rcLocal.mBottom + height;
+
+ // get textbox a chance to reshape its content
+ mNoVisibleTabsHelpText->reshape(width, height, called_from_parent);
+
+ setRect(rcLocal);
+
+ // assume that help text is always fit accordion.
+ // necessary text paddings can be set via h_pad and v_pad
+ mNoVisibleTabsHelpText->setRect(getLocalRect());
+
+ arrange();
+}
+
+//---------------------------------------------------------------------------------
+BOOL LLAccordionCtrl::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+ return LLPanel::handleRightMouseDown(x, y, mask);
+}
+
+//---------------------------------------------------------------------------------
+void LLAccordionCtrl::shiftAccordionTabs(S16 panel_num, S32 delta)
+{
+ for(size_t i = panel_num; i < mAccordionTabs.size(); i++ )
+ {
+ ctrlShiftVertical(mAccordionTabs[i],delta);
+ }
+}
+
+
+//---------------------------------------------------------------------------------
+void LLAccordionCtrl::onCollapseCtrlCloseOpen(S16 panel_num)
+{
+ if(mSingleExpansion)
+ {
+ for(size_t i=0;i<mAccordionTabs.size();++i)
+ {
+ if(i==panel_num)
+ continue;
+ if(mAccordionTabs[i]->getDisplayChildren())
+ mAccordionTabs[i]->setDisplayChildren(false);
+ }
+
+ }
+ arrange();
+}
+
+void LLAccordionCtrl::show_hide_scrollbar(S32 width, S32 height)
+{
+ calcRecuiredHeight();
+ if(getRecuiredHeight() > height )
+ showScrollbar(width,height);
+ else
+ hideScrollbar(width,height);
+}
+
+void LLAccordionCtrl::showScrollbar(S32 width, S32 height)
+{
+ bool was_visible = mScrollbar->getVisible();
+
+ mScrollbar->setVisible(true);
+
+ static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
+
+ ctrlSetLeftTopAndSize(mScrollbar
+ ,width-scrollbar_size - PARENT_BORDER_MARGIN/2
+ ,height-PARENT_BORDER_MARGIN
+ ,scrollbar_size
+ ,height-2*PARENT_BORDER_MARGIN);
+
+ mScrollbar->setPageSize(height);
+ mScrollbar->setDocParams(mInnerRect.getHeight(),mScrollbar->getDocPos());
+
+ if(was_visible)
+ {
+ S32 scroll_pos = llmin(mScrollbar->getDocPos(), getRecuiredHeight() - height - 1);
+ mScrollbar->setDocPos(scroll_pos);
+ }
+}
+
+void LLAccordionCtrl::hideScrollbar( S32 width, S32 height )
+{
+ if(mScrollbar->getVisible() == false)
+ return;
+ mScrollbar->setVisible(false);
+
+ static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
+
+ S32 panel_width = width - 2*BORDER_MARGIN;
+
+ //reshape all accordeons and shift all draggers
+ for(size_t i=0;i<mAccordionTabs.size();++i)
+ {
+ LLRect panel_rect = mAccordionTabs[i]->getRect();
+ ctrlSetLeftTopAndSize(mAccordionTabs[i],panel_rect.mLeft,panel_rect.mTop,panel_width,panel_rect.getHeight());
+ }
+
+ mScrollbar->setDocPos(0);
+
+ if(mAccordionTabs.size()>0)
+ {
+ S32 panel_top = height - BORDER_MARGIN; // Top coordinate of the first panel
+ S32 diff = panel_top - mAccordionTabs[0]->getRect().mTop;
+ shiftAccordionTabs(0,diff);
+ }
+}
+
+
+//---------------------------------------------------------------------------------
+S32 LLAccordionCtrl::calcRecuiredHeight()
+{
+ S32 rec_height = 0;
+
+ std::vector<LLAccordionCtrlTab*>::iterator panel;
+ for(panel=mAccordionTabs.begin(); panel!=mAccordionTabs.end(); ++panel)
+ {
+ LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(*panel);
+ if(accordion_tab && accordion_tab->getVisible())
+ {
+ rec_height += accordion_tab->getRect().getHeight();
+ }
+ }
+
+ mInnerRect.setLeftTopAndSize(0,rec_height + BORDER_MARGIN*2,getRect().getWidth(),rec_height + BORDER_MARGIN);
+
+ return mInnerRect.getHeight();
+}
+
+//---------------------------------------------------------------------------------
+void LLAccordionCtrl::ctrlSetLeftTopAndSize(LLView* panel, S32 left, S32 top, S32 width, S32 height)
+{
+ if(!panel)
+ return;
+ LLRect panel_rect = panel->getRect();
+ panel_rect.setLeftTopAndSize( left, top, width, height);
+ panel->reshape( width, height, 1);
+ panel->setRect(panel_rect);
+}
+
+void LLAccordionCtrl::ctrlShiftVertical(LLView* panel,S32 delta)
+{
+ if(!panel)
+ return;
+ panel->translate(0,delta);
+}
+
+//---------------------------------------------------------------------------------
+
+void LLAccordionCtrl::addCollapsibleCtrl(LLView* view)
+{
+ LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(view);
+ if(!accordion_tab)
+ return;
+ if(std::find(beginChild(), endChild(), accordion_tab) == endChild())
+ addChild(accordion_tab);
+ mAccordionTabs.push_back(accordion_tab);
+
+ accordion_tab->setDropDownStateChangedCallback( boost::bind(&LLAccordionCtrl::onCollapseCtrlCloseOpen, this, mAccordionTabs.size() - 1) );
+ arrange();
+}
+
+void LLAccordionCtrl::removeCollapsibleCtrl(LLView* view)
+{
+ 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;
+ }
+ }
+
+ // if removed is selected - reset selection
+ if (mSelectedTab == view)
+ {
+ mSelectedTab = NULL;
+ }
+}
+
+void LLAccordionCtrl::initNoTabsWidget(const LLTextBox::Params& tb_params)
+{
+ LLTextBox::Params tp = tb_params;
+ tp.rect(getLocalRect());
+ mNoMatchedTabsOrigString = tp.initial_value().asString();
+ mNoVisibleTabsHelpText = LLUICtrlFactory::create<LLTextBox>(tp, this);
+}
+
+void LLAccordionCtrl::updateNoTabsHelpTextVisibility()
+{
+ bool visible_exists = false;
+ std::vector<LLAccordionCtrlTab*>::const_iterator it = mAccordionTabs.begin();
+ const std::vector<LLAccordionCtrlTab*>::const_iterator it_end = mAccordionTabs.end();
+ for (; it != it_end; ++it)
+ {
+ if ((*it)->getVisible())
+ {
+ visible_exists = true;
+ break;
+ }
+ }
+
+ mNoVisibleTabsHelpText->setVisible(!visible_exists);
+}
+
+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;
+
+ S32 collapsed_height = 0;
+
+ for(size_t i=0;i<mAccordionTabs.size();++i)
+ {
+ LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[i]);
+
+ if(accordion_tab->getVisible() == false) //skip hidden accordion tabs
+ continue;
+ if(!accordion_tab->isExpanded() )
+ {
+ collapsed_height+=mAccordionTabs[i]->getRect().getHeight();
+ }
+ }
+
+ S32 expanded_height = getRect().getHeight() - BORDER_MARGIN - collapsed_height;
+
+ for(size_t i=0;i<mAccordionTabs.size();++i)
+ {
+ LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[i]);
+
+ 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]);
+
+ if(accordion_tab->getVisible() == false) //skip hidden accordion tabs
+ continue;
+
+ if(!accordion_tab->isExpanded() )
+ {
+ ctrlSetLeftTopAndSize(mAccordionTabs[i], panel_left, panel_top, panel_width, accordion_tab->getRect().getHeight());
+ panel_top-=mAccordionTabs[i]->getRect().getHeight();
+ }
+ else
+ {
+ S32 panel_height = accordion_tab->getRect().getHeight();
+
+ if(mFitParent)
+ {
+ // all expanded tabs will have equal height
+ panel_height = calcExpandedTabHeight(i, panel_top);
+ ctrlSetLeftTopAndSize(accordion_tab, panel_left, panel_top, panel_width, panel_height);
+
+ // try to make accordion tab fit accordion view height.
+ // Accordion View should implement getRequiredRect() and provide valid height
+ S32 optimal_height = accordion_tab->getAccordionView()->getRequiredRect().getHeight();
+ optimal_height += accordion_tab->getHeaderHeight() + 2 * BORDER_MARGIN;
+ if(optimal_height < panel_height)
+ {
+ panel_height = optimal_height;
+ }
+
+ // minimum tab height is equal to header height
+ if(mAccordionTabs[i]->getHeaderHeight() > panel_height)
+ {
+ panel_height = mAccordionTabs[i]->getHeaderHeight();
+ }
+ }
+
+ ctrlSetLeftTopAndSize(mAccordionTabs[i], panel_left, panel_top, panel_width, panel_height);
+ panel_top-=panel_height;
+
+ }
+ }
+
+ show_hide_scrollbar(getRect().getWidth(),getRect().getHeight());
+
+ updateLayout(getRect().getWidth(),getRect().getHeight());
+}
+
+
+void LLAccordionCtrl::arrange()
+{
+ updateNoTabsHelpTextVisibility();
+
+ if( mAccordionTabs.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;
+
+ if (accordion_tab->getFitParent())
+ panel_height = accordion_tab->getRect().getHeight();
+ ctrlSetLeftTopAndSize(accordion_tab,panel_rect.mLeft,panel_top,panel_width,panel_height);
+
+ show_hide_scrollbar(getRect().getWidth(),getRect().getHeight());
+ return;
+
+ }
+
+ if(mSingleExpansion)
+ arrangeSinge ();
+ else
+ arrangeMultiple ();
+}
+
+//---------------------------------------------------------------------------------
+
+BOOL LLAccordionCtrl::handleScrollWheel ( S32 x, S32 y, S32 clicks )
+{
+ if(LLPanel::handleScrollWheel(x,y,clicks))
+ return TRUE;
+ if( mScrollbar->getVisible() && mScrollbar->handleScrollWheel( 0, 0, clicks ) )
+ return TRUE;
+ return false;
+
+}
+
+BOOL LLAccordionCtrl::handleKeyHere (KEY key, MASK mask)
+{
+ if( mScrollbar->getVisible() && mScrollbar->handleKeyHere( key,mask ) )
+ return TRUE;
+ return LLPanel::handleKeyHere(key,mask);
+}
+
+BOOL LLAccordionCtrl::handleDragAndDrop (S32 x, S32 y, MASK mask,
+ BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ std::string& tooltip_msg)
+{
+ // Scroll folder view if needed. Never accepts a drag or drop.
+ *accept = ACCEPT_NO;
+ BOOL handled = autoScroll(x, y);
+
+ if( !handled )
+ {
+ handled = childrenHandleDragAndDrop(x, y, mask, drop, cargo_type,
+ cargo_data, accept, tooltip_msg) != NULL;
+ }
+ return TRUE;
+}
+
+BOOL LLAccordionCtrl::autoScroll (S32 x, S32 y)
+{
+ static LLUICachedControl<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 ;
+ if(mScrollbar->getVisible())
+ panel_top+=mScrollbar->getDocPos();
+
+ S32 panel_width = width - 2*BORDER_MARGIN;
+
+ static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
+ if(mScrollbar->getVisible())
+ panel_width-=scrollbar_size;
+
+ //set sizes for first panels and dragbars
+ for(size_t i=0;i<mAccordionTabs.size();++i)
+ {
+ if(!mAccordionTabs[i]->getVisible())
+ continue;
+ LLRect panel_rect = mAccordionTabs[i]->getRect();
+ ctrlSetLeftTopAndSize(mAccordionTabs[i],panel_rect.mLeft,panel_top,panel_width,panel_rect.getHeight());
+ panel_top-=panel_rect.getHeight();
+ }
+}
+
+void LLAccordionCtrl::onScrollPosChangeCallback(S32, LLScrollbar*)
+{
+ updateLayout(getRect().getWidth(),getRect().getHeight());
+}
+void LLAccordionCtrl::onOpen (const LLSD& key)
+{
+ for(size_t i=0;i<mAccordionTabs.size();++i)
+ {
+ LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[i]);
+ LLPanel* panel = dynamic_cast<LLPanel*>(accordion_tab->getAccordionView());
+ if(panel!=NULL)
+ {
+ panel->onOpen(key);
+ }
+ }
+}
+S32 LLAccordionCtrl::notifyParent(const LLSD& info)
+{
+ if(info.has("action"))
+ {
+ std::string str_action = info["action"];
+ if(str_action == "size_changes")
+ {
+ //
+ arrange();
+ return 1;
+ }
+ else if(str_action == "select_next")
+ {
+ for(size_t i=0;i<mAccordionTabs.size();++i)
+ {
+ LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[i]);
+ if(accordion_tab->hasFocus())
+ {
+ while(++i<mAccordionTabs.size())
+ {
+ if(mAccordionTabs[i]->getVisible())
+ break;
+ }
+ if(i<mAccordionTabs.size())
+ {
+ accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[i]);
+ accordion_tab->notify(LLSD().with("action","select_first"));
+ return 1;
+ }
+ break;
+ }
+ }
+ return 0;
+ }
+ else if(str_action == "select_prev")
+ {
+ for(size_t i=0;i<mAccordionTabs.size();++i)
+ {
+ 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;
+ }
+ break;
+ }
+ }
+ return 0;
+ }
+ else if(str_action == "select_current")
+ {
+ for(size_t i=0;i<mAccordionTabs.size();++i)
+ {
+ // Set selection to the currently focused tab.
+ if(mAccordionTabs[i]->hasFocus())
+ {
+ if (mAccordionTabs[i] != mSelectedTab)
+ {
+ if (mSelectedTab)
+ {
+ mSelectedTab->setSelected(false);
+ }
+ mSelectedTab = mAccordionTabs[i];
+ mSelectedTab->setSelected(true);
+ }
+
+ return 1;
+ }
+ }
+ return 0;
+ }
+ else if(str_action == "deselect_current")
+ {
+ // Reset selection to the currently selected tab.
+ if (mSelectedTab)
+ {
+ mSelectedTab->setSelected(false);
+ mSelectedTab = NULL;
+ return 1;
+ }
+ return 0;
+ }
+ }
+ else if (info.has("scrollToShowRect"))
+ {
+ LLRect screen_rc, local_rc;
+ screen_rc.setValue(info["scrollToShowRect"]);
+ screenRectToLocal(screen_rc, &local_rc);
+
+ // Translate to parent coordinatess to check if we are in visible rectangle
+ local_rc.translate( getRect().mLeft, getRect().mBottom );
+
+ if ( !getRect().contains (local_rc) )
+ {
+ // Back to local coords and calculate position for scroller
+ S32 bottom = mScrollbar->getDocPos() - local_rc.mBottom + getRect().mBottom;
+ S32 top = mScrollbar->getDocPos() - local_rc.mTop + getRect().mTop;
+
+ S32 scroll_pos = llclamp(mScrollbar->getDocPos(),
+ bottom, // min vertical scroll
+ top); // max vertical scroll
+
+ mScrollbar->setDocPos( scroll_pos );
+ }
+ return 1;
+ }
+ else if (info.has("child_visibility_change"))
+ {
+ BOOL new_visibility = info["child_visibility_change"];
+ if (new_visibility)
+ {
+ // there is at least one visible tab
+ mNoVisibleTabsHelpText->setVisible(FALSE);
+ }
+ else
+ {
+ // it could be the latest visible tab, check all of them
+ updateNoTabsHelpTextVisibility();
+ }
+ }
+ return LLPanel::notifyParent(info);
+}
+void LLAccordionCtrl::reset ()
+{
+ if(mScrollbar)
+ mScrollbar->setDocPos(0);
+}
+
+void LLAccordionCtrl::expandDefaultTab()
+{
+ if (mAccordionTabs.size() > 0)
+ {
+ LLAccordionCtrlTab* tab = mAccordionTabs.front();
+
+ if (!tab->getDisplayChildren())
+ {
+ tab->setDisplayChildren(true);
+ }
+
+ for (size_t i = 1; i < mAccordionTabs.size(); ++i)
+ {
+ tab = mAccordionTabs[i];
+
+ if (tab->getDisplayChildren())
+ {
+ tab->setDisplayChildren(false);
+ }
+ }
+
+ arrange();
+ }
+}
+
+void LLAccordionCtrl::sort()
+{
+ if (!mTabComparator)
+ {
+ llwarns << "No comparator specified for sorting accordion tabs." << llendl;
+ return;
+ }
+
+ std::sort(mAccordionTabs.begin(), mAccordionTabs.end(), LLComparatorAdaptor(*mTabComparator));
+ arrange();
+}
+
+void LLAccordionCtrl::setFilterSubString(const std::string& filter_string)
+{
+ LLStringUtil::format_map_t args;
+ args["[SEARCH_TERM]"] = LLURI::escape(filter_string);
+ std::string text = filter_string.empty() ? mNoVisibleTabsOrigString : mNoMatchedTabsOrigString;
+ LLStringUtil::format(text, args);
+
+ mNoVisibleTabsHelpText->setValue(text);
+}
+
+const LLAccordionCtrlTab* LLAccordionCtrl::getExpandedTab() const
+{
+ typedef std::vector<LLAccordionCtrlTab*>::const_iterator tabs_const_iterator;
+
+ const LLAccordionCtrlTab* result = 0;
+
+ for (tabs_const_iterator i = mAccordionTabs.begin(); i != mAccordionTabs.end(); ++i)
+ {
+ if ((*i)->isExpanded())
+ {
+ result = *i;
+ break;
+ }
+ }
+
+ return result;
+}
+
+S32 LLAccordionCtrl::calcExpandedTabHeight(S32 tab_index /* = 0 */, S32 available_height /* = 0 */)
+{
+ if(tab_index < 0)
+ {
+ return available_height;
+ }
+
+ S32 collapsed_tabs_height = 0;
+ S32 num_expanded = 0;
+
+ for(size_t n = tab_index; n < mAccordionTabs.size(); ++n)
+ {
+ if(!mAccordionTabs[n]->isExpanded())
+ {
+ collapsed_tabs_height += mAccordionTabs[n]->getHeaderHeight();
+ }
+ else
+ {
+ ++num_expanded;
+ }
+ }
+
+ if(0 == num_expanded)
+ {
+ return available_height;
+ }
+
+ S32 expanded_tab_height = available_height - collapsed_tabs_height - BORDER_MARGIN; // top BORDER_MARGIN is added in arrange(), here we add bottom BORDER_MARGIN
+ expanded_tab_height /= num_expanded;
+ return expanded_tab_height;
+}
diff --git a/indra/llui/llaccordionctrl.h b/indra/llui/llaccordionctrl.h
new file mode 100644
index 0000000000..1fe64c472e
--- /dev/null
+++ b/indra/llui/llaccordionctrl.h
@@ -0,0 +1,193 @@
+/**
+ * @file LLAccordionCtrl.h
+ * @brief Accordion Panel implementation
+ *
+ * $LicenseInfo:firstyear=2004&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_ACCORDIONCTRL_H
+#define LL_ACCORDIONCTRL_H
+
+#include "llpanel.h"
+#include "lltextbox.h"
+#include "llscrollbar.h"
+
+#include <vector>
+#include <algorithm>
+#include <string>
+
+class LLAccordionCtrlTab;
+
+class LLAccordionCtrl: public LLPanel
+{
+private:
+
+ std::vector<LLAccordionCtrlTab*> mAccordionTabs;
+
+ void ctrlSetLeftTopAndSize(LLView* panel, S32 left, S32 top, S32 width, S32 height);
+ void ctrlShiftVertical(LLView* panel,S32 delta);
+
+ void onCollapseCtrlCloseOpen(S16 panel_num);
+ void shiftAccordionTabs(S16 panel_num, S32 delta);
+
+
+public:
+ /**
+ * Abstract comparator for accordion tabs.
+ */
+ class LLTabComparator
+ {
+ public:
+ LLTabComparator() {};
+ virtual ~LLTabComparator() {};
+
+ /** Returns true if tab1 < tab2, false otherwise */
+ virtual bool compare(const LLAccordionCtrlTab* tab1, const LLAccordionCtrlTab* tab2) const = 0;
+ };
+
+ struct Params
+ : public LLInitParam::Block<Params, LLPanel::Params>
+ {
+ Optional<bool> single_expansion,
+ fit_parent; /* Accordion will fit its parent size, controls that are placed into
+ accordion tabs are responsible for scrolling their content.
+ *NOTE fit_parent works best when combined with single_expansion.
+ Accordion view should implement getRequiredRect() and provide valid height*/
+ Optional<LLTextBox::Params> no_matched_tabs_text;
+ Optional<LLTextBox::Params> no_visible_tabs_text;
+
+ Params()
+ : single_expansion("single_expansion",false)
+ , fit_parent("fit_parent", false)
+ , no_matched_tabs_text("no_matched_tabs_text")
+ , no_visible_tabs_text("no_visible_tabs_text")
+ {};
+ };
+
+ LLAccordionCtrl(const Params& params);
+
+ LLAccordionCtrl();
+ virtual ~LLAccordionCtrl();
+
+ virtual BOOL postBuild();
+
+ virtual BOOL handleRightMouseDown ( S32 x, S32 y, MASK mask);
+ virtual BOOL handleScrollWheel ( S32 x, S32 y, S32 clicks );
+ virtual BOOL handleKeyHere (KEY key, MASK mask);
+ virtual BOOL handleDragAndDrop (S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ std::string& tooltip_msg);
+ //
+
+ // Call reshape after changing splitter's size
+ virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
+
+ void addCollapsibleCtrl(LLView* view);
+ void removeCollapsibleCtrl(LLView* view);
+ void arrange();
+
+
+ void draw();
+
+ void onScrollPosChangeCallback(S32, LLScrollbar*);
+
+ void onOpen (const LLSD& key);
+ S32 notifyParent(const LLSD& info);
+
+ void reset ();
+ void expandDefaultTab();
+
+ void setComparator(const LLTabComparator* comp) { mTabComparator = comp; }
+ void sort();
+
+ /**
+ * Sets filter substring as a search_term for help text when there are no any visible tabs.
+ */
+ void setFilterSubString(const std::string& filter_string);
+
+ /**
+ * This method returns the first expanded accordion tab.
+ * It is expected to be called for accordion which doesn't allow multiple
+ * tabs to be expanded. Use with care.
+ */
+ const LLAccordionCtrlTab* getExpandedTab() const;
+
+ LLAccordionCtrlTab* getSelectedTab() const { return mSelectedTab; }
+
+ bool getFitParent() const {return mFitParent;}
+
+private:
+ void initNoTabsWidget(const LLTextBox::Params& tb_params);
+ void updateNoTabsHelpTextVisibility();
+
+ void arrangeSinge();
+ void arrangeMultiple();
+
+ // Calc Splitter's height that is necessary to display all child content
+ S32 calcRecuiredHeight();
+ S32 getRecuiredHeight() const { return mInnerRect.getHeight(); }
+ S32 calcExpandedTabHeight(S32 tab_index = 0, S32 available_height = 0);
+
+ void updateLayout (S32 width, S32 height);
+
+ void show_hide_scrollbar (S32 width, S32 height);
+
+ void showScrollbar (S32 width, S32 height);
+ void hideScrollbar (S32 width, S32 height);
+
+ BOOL autoScroll (S32 x, S32 y);
+
+ /**
+ * An adaptor for LLTabComparator
+ */
+ struct LLComparatorAdaptor
+ {
+ LLComparatorAdaptor(const LLTabComparator& comparator) : mComparator(comparator) {};
+
+ bool operator()(const LLAccordionCtrlTab* tab1, const LLAccordionCtrlTab* tab2)
+ {
+ return mComparator.compare(tab1, tab2);
+ }
+
+ const LLTabComparator& mComparator;
+ };
+
+private:
+ LLRect mInnerRect;
+ LLScrollbar* mScrollbar;
+ bool mSingleExpansion;
+ bool mFitParent;
+ bool mAutoScrolling;
+ F32 mAutoScrollRate;
+ LLTextBox* mNoVisibleTabsHelpText;
+
+ std::string mNoMatchedTabsOrigString;
+ std::string mNoVisibleTabsOrigString;
+
+ LLAccordionCtrlTab* mSelectedTab;
+ const LLTabComparator* mTabComparator;
+};
+
+
+#endif // LL_LLSPLITTER_H
diff --git a/indra/llui/llaccordionctrltab.cpp b/indra/llui/llaccordionctrltab.cpp
new file mode 100644
index 0000000000..b7da5f4a1b
--- /dev/null
+++ b/indra/llui/llaccordionctrltab.cpp
@@ -0,0 +1,1065 @@
+/**
+ * @file LLAccordionCtrlTab.cpp
+ * @brief Collapsible control implementation
+ *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llaccordionctrltab.h"
+#include "llaccordionctrl.h"
+
+#include "lllocalcliprect.h"
+#include "llscrollbar.h"
+#include "lltextbox.h"
+#include "lltextutil.h"
+#include "lluictrl.h"
+
+static const std::string DD_BUTTON_NAME = "dd_button";
+static const std::string DD_TEXTBOX_NAME = "dd_textbox";
+static const std::string DD_HEADER_NAME = "dd_header";
+
+static const S32 HEADER_HEIGHT = 23;
+static const S32 HEADER_IMAGE_LEFT_OFFSET = 5;
+static const S32 HEADER_TEXT_LEFT_OFFSET = 30;
+static const F32 AUTO_OPEN_TIME = 1.f;
+static const S32 VERTICAL_MULTIPLE = 16;
+static const S32 PARENT_BORDER_MARGIN = 5;
+
+static LLDefaultChildRegistry::Register<LLAccordionCtrlTab> t1("accordion_tab");
+
+class LLAccordionCtrlTab::LLAccordionCtrlTabHeader : public LLUICtrl
+{
+public:
+ friend class LLUICtrlFactory;
+
+ struct Params : public LLInitParam::Block<Params, LLAccordionCtrlTab::Params>
+ {
+ Params();
+ };
+
+ LLAccordionCtrlTabHeader(const LLAccordionCtrlTabHeader::Params& p);
+
+ virtual ~LLAccordionCtrlTabHeader();
+
+ virtual void draw();
+
+ virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
+
+ virtual BOOL postBuild();
+
+ std::string getTitle();
+ void setTitle(const std::string& title, const std::string& hl);
+
+ void setTitleFontStyle(std::string style);
+
+ void setTitleColor(LLUIColor);
+
+ void setSelected(bool is_selected) { mIsSelected = is_selected; }
+
+ virtual void onMouseEnter(S32 x, S32 y, MASK mask);
+ virtual void onMouseLeave(S32 x, S32 y, MASK mask);
+ virtual BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent);
+ virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ std::string& tooltip_msg);
+private:
+
+ LLTextBox* mHeaderTextbox;
+
+ // Overlay images (arrows)
+ LLPointer<LLUIImage> mImageCollapsed;
+ LLPointer<LLUIImage> mImageExpanded;
+ LLPointer<LLUIImage> mImageCollapsedPressed;
+ LLPointer<LLUIImage> mImageExpandedPressed;
+
+ // Background images
+ LLPointer<LLUIImage> mImageHeader;
+ LLPointer<LLUIImage> mImageHeaderOver;
+ LLPointer<LLUIImage> mImageHeaderPressed;
+ LLPointer<LLUIImage> mImageHeaderFocused;
+
+ // style saved when applying it in setTitleFontStyle
+ LLStyle::Params mStyleParams;
+
+ LLUIColor mHeaderBGColor;
+
+ bool mNeedsHighlight;
+ bool mIsSelected;
+
+ LLFrameTimer mAutoOpenTimer;
+};
+
+LLAccordionCtrlTab::LLAccordionCtrlTabHeader::Params::Params()
+{
+}
+
+LLAccordionCtrlTab::LLAccordionCtrlTabHeader::LLAccordionCtrlTabHeader(
+ const LLAccordionCtrlTabHeader::Params& p)
+: LLUICtrl(p)
+, mHeaderBGColor(p.header_bg_color())
+, mNeedsHighlight(false)
+, mIsSelected(false),
+ mImageCollapsed(p.header_collapse_img),
+ mImageCollapsedPressed(p.header_collapse_img_pressed),
+ mImageExpanded(p.header_expand_img),
+ mImageExpandedPressed(p.header_expand_img_pressed),
+ mImageHeader(p.header_image),
+ mImageHeaderOver(p.header_image_over),
+ mImageHeaderPressed(p.header_image_pressed),
+ mImageHeaderFocused(p.header_image_focused)
+{
+ LLTextBox::Params textboxParams;
+ textboxParams.name(DD_TEXTBOX_NAME);
+ textboxParams.initial_value(p.title());
+ textboxParams.text_color(p.header_text_color());
+ textboxParams.follows.flags(FOLLOWS_NONE);
+ textboxParams.font( p.font() );
+ textboxParams.font_shadow(LLFontGL::NO_SHADOW);
+ textboxParams.use_ellipses = true;
+ textboxParams.bg_visible = false;
+ textboxParams.mouse_opaque = false;
+ mHeaderTextbox = LLUICtrlFactory::create<LLTextBox>(textboxParams);
+ addChild(mHeaderTextbox);
+}
+
+LLAccordionCtrlTab::LLAccordionCtrlTabHeader::~LLAccordionCtrlTabHeader()
+{
+}
+
+BOOL LLAccordionCtrlTab::LLAccordionCtrlTabHeader::postBuild()
+{
+ return TRUE;
+}
+
+std::string LLAccordionCtrlTab::LLAccordionCtrlTabHeader::getTitle()
+{
+ if(mHeaderTextbox)
+ {
+ return mHeaderTextbox->getText();
+ }
+ else
+ {
+ return LLStringUtil::null;
+ }
+}
+
+void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::setTitle(const std::string& title, const std::string& hl)
+{
+ if(mHeaderTextbox)
+ {
+ LLTextUtil::textboxSetHighlightedVal(
+ mHeaderTextbox,
+ mStyleParams,
+ title,
+ hl);
+ }
+}
+
+void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::setTitleFontStyle(std::string style)
+{
+ if (mHeaderTextbox)
+ {
+ std::string text = mHeaderTextbox->getText();
+ mStyleParams.font(mHeaderTextbox->getDefaultFont());
+ mStyleParams.font.style(style);
+ mHeaderTextbox->setText(text, mStyleParams);
+ }
+}
+
+void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::setTitleColor(LLUIColor color)
+{
+ if(mHeaderTextbox)
+ {
+ mHeaderTextbox->setColor(color);
+ }
+}
+
+void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::draw()
+{
+ S32 width = getRect().getWidth();
+ S32 height = getRect().getHeight();
+
+ gl_rect_2d(0,0,width - 1 ,height - 1,mHeaderBGColor.get(),true);
+
+ LLAccordionCtrlTab* parent = dynamic_cast<LLAccordionCtrlTab*>(getParent());
+ bool collapsible = (parent && parent->getCollapsible());
+ bool expanded = (parent && parent->getDisplayChildren());
+
+ // Handle overlay images, if needed
+ // Only show green "focus" background image if the accordion is open,
+ // because the user's mental model of focus is that it goes away after
+ // the accordion is closed.
+ if (getParent()->hasFocus() || mIsSelected
+ /*&& !(collapsible && !expanded)*/ // WHY??
+ )
+ {
+ mImageHeaderFocused->draw(0,0,width,height);
+ }
+ else
+ {
+ mImageHeader->draw(0,0,width,height);
+ }
+
+ if(mNeedsHighlight)
+ {
+ mImageHeaderOver->draw(0,0,width,height);
+ }
+
+
+ if(collapsible)
+ {
+ LLPointer<LLUIImage> overlay_image;
+ if(expanded)
+ {
+ overlay_image = mImageExpanded;
+ }
+ else
+ {
+ overlay_image = mImageCollapsed;
+ }
+ overlay_image->draw(HEADER_IMAGE_LEFT_OFFSET,
+ (height - overlay_image->getHeight()) / 2);
+ }
+
+ LLUICtrl::draw();
+}
+
+void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::reshape(S32 width, S32 height, BOOL called_from_parent /* = TRUE */)
+{
+ S32 header_height = mHeaderTextbox->getTextPixelHeight();
+
+ LLRect textboxRect(HEADER_TEXT_LEFT_OFFSET,(height+header_height)/2 ,width,(height-header_height)/2);
+ mHeaderTextbox->reshape(textboxRect.getWidth(), textboxRect.getHeight());
+ mHeaderTextbox->setRect(textboxRect);
+
+ if (mHeaderTextbox->getTextPixelWidth() > mHeaderTextbox->getRect().getWidth())
+ {
+ setToolTip(mHeaderTextbox->getText());
+ }
+ else
+ {
+ setToolTip(LLStringUtil::null);
+ }
+}
+
+void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::onMouseEnter(S32 x, S32 y, MASK mask)
+{
+ LLUICtrl::onMouseEnter(x, y, mask);
+ mNeedsHighlight = true;
+}
+void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::onMouseLeave(S32 x, S32 y, MASK mask)
+{
+ LLUICtrl::onMouseLeave(x, y, mask);
+ mNeedsHighlight = false;
+ mAutoOpenTimer.stop();
+}
+BOOL LLAccordionCtrlTab::LLAccordionCtrlTabHeader::handleKey(KEY key, MASK mask, BOOL called_from_parent)
+{
+ if ( ( key == KEY_LEFT || key == KEY_RIGHT) && mask == MASK_NONE)
+ {
+ return getParent()->handleKey(key, mask, called_from_parent);
+ }
+ return LLUICtrl::handleKey(key, mask, called_from_parent);
+}
+BOOL LLAccordionCtrlTab::LLAccordionCtrlTabHeader::handleDragAndDrop(S32 x, S32 y, MASK mask,
+ BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ std::string& tooltip_msg)
+{
+ LLAccordionCtrlTab* parent = dynamic_cast<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)
+ ,header_height("header_height", HEADER_HEIGHT),
+ min_width("min_width", 0),
+ min_height("min_height", 0)
+ ,collapsible("collapsible", true)
+ ,header_bg_color("header_bg_color")
+ ,dropdown_bg_color("dropdown_bg_color")
+ ,header_visible("header_visible",true)
+ ,padding_left("padding_left",2)
+ ,padding_right("padding_right",2)
+ ,padding_top("padding_top",2)
+ ,padding_bottom("padding_bottom",2)
+ ,header_expand_img("header_expand_img")
+ ,header_expand_img_pressed("header_expand_img_pressed")
+ ,header_collapse_img("header_collapse_img")
+ ,header_collapse_img_pressed("header_collapse_img_pressed")
+ ,header_image("header_image")
+ ,header_image_over("header_image_over")
+ ,header_image_pressed("header_image_pressed")
+ ,header_image_focused("header_image_focused")
+ ,header_text_color("header_text_color")
+ ,fit_panel("fit_panel",true)
+ ,selection_enabled("selection_enabled", false)
+{
+ mouse_opaque(false);
+}
+
+LLAccordionCtrlTab::LLAccordionCtrlTab(const LLAccordionCtrlTab::Params&p)
+ : LLUICtrl(p)
+ ,mDisplayChildren(p.display_children)
+ ,mCollapsible(p.collapsible)
+ ,mExpandedHeight(0)
+ ,mDropdownBGColor(p.dropdown_bg_color())
+ ,mHeaderVisible(p.header_visible)
+ ,mPaddingLeft(p.padding_left)
+ ,mPaddingRight(p.padding_right)
+ ,mPaddingTop(p.padding_top)
+ ,mPaddingBottom(p.padding_bottom)
+ ,mCanOpenClose(true)
+ ,mFitPanel(p.fit_panel)
+ ,mSelectionEnabled(p.selection_enabled)
+ ,mContainerPanel(NULL)
+ ,mScrollbar(NULL)
+{
+ mStoredOpenCloseState = false;
+ mWasStateStored = false;
+
+ mDropdownBGColor = LLColor4::white;
+ LLAccordionCtrlTabHeader::Params headerParams;
+ headerParams.name(DD_HEADER_NAME);
+ headerParams.title(p.title);
+ mHeader = LLUICtrlFactory::create<LLAccordionCtrlTabHeader>(headerParams);
+ addChild(mHeader, 1);
+
+ LLFocusableElement::setFocusReceivedCallback(boost::bind(&LLAccordionCtrlTab::selectOnFocusReceived, this));
+
+ if (!p.selection_enabled)
+ {
+ LLFocusableElement::setFocusLostCallback(boost::bind(&LLAccordionCtrlTab::deselectOnFocusLost, this));
+ }
+
+ reshape(100, 200,FALSE);
+}
+
+LLAccordionCtrlTab::~LLAccordionCtrlTab()
+{
+}
+
+
+void LLAccordionCtrlTab::setDisplayChildren(bool display)
+{
+ mDisplayChildren = display;
+ LLRect rect = getRect();
+
+ rect.mBottom = rect.mTop - (getDisplayChildren() ?
+ mExpandedHeight : HEADER_HEIGHT);
+ setRect(rect);
+
+ if(mContainerPanel)
+ mContainerPanel->setVisible(getDisplayChildren());
+
+ if(mDisplayChildren)
+ {
+ adjustContainerPanel();
+ }
+ else
+ {
+ if(mScrollbar)
+ mScrollbar->setVisible(false);
+ }
+
+}
+
+void LLAccordionCtrlTab::reshape(S32 width, S32 height, BOOL called_from_parent /* = TRUE */)
+{
+ LLRect headerRect;
+
+ headerRect.setLeftTopAndSize(
+ 0,height,width,HEADER_HEIGHT);
+ mHeader->setRect(headerRect);
+ mHeader->reshape(headerRect.getWidth(), headerRect.getHeight());
+
+ if(!mDisplayChildren)
+ return;
+
+ LLRect childRect;
+
+ childRect.setLeftTopAndSize(
+ getPaddingLeft(),
+ height - getHeaderHeight() - getPaddingTop(),
+ width - getPaddingLeft() - getPaddingRight(),
+ height - getHeaderHeight() - getPaddingTop() - getPaddingBottom() );
+
+ adjustContainerPanel(childRect);
+}
+
+void LLAccordionCtrlTab::changeOpenClose(bool is_open)
+{
+ if(is_open)
+ mExpandedHeight = getRect().getHeight();
+
+ setDisplayChildren(!is_open);
+ reshape(getRect().getWidth(), getRect().getHeight(), FALSE);
+ if (mCommitSignal)
+ {
+ (*mCommitSignal)(this, getDisplayChildren());
+ }
+}
+
+void LLAccordionCtrlTab::handleVisibilityChange(BOOL new_visibility)
+{
+ LLUICtrl::handleVisibilityChange(new_visibility);
+
+ notifyParent(LLSD().with("child_visibility_change", new_visibility));
+}
+
+BOOL LLAccordionCtrlTab::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ if(mCollapsible && mHeaderVisible && mCanOpenClose)
+ {
+ if(y >= (getRect().getHeight() - HEADER_HEIGHT) )
+ {
+ LLAccordionCtrlTabHeader* header = getChild<LLAccordionCtrlTabHeader>(DD_HEADER_NAME);
+ header->setFocus(true);
+ changeOpenClose(getDisplayChildren());
+
+ //reset stored state
+ mWasStateStored = false;
+ return TRUE;
+ }
+ }
+ return LLUICtrl::handleMouseDown(x,y,mask);
+}
+
+BOOL LLAccordionCtrlTab::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ return LLUICtrl::handleMouseUp(x,y,mask);
+}
+
+boost::signals2::connection LLAccordionCtrlTab::setDropDownStateChangedCallback(commit_callback_t cb)
+{
+ return setCommitCallback(cb);
+}
+
+bool LLAccordionCtrlTab::addChild(LLView* child, S32 tab_group)
+{
+ if(DD_HEADER_NAME != child->getName())
+ {
+ reshape(child->getRect().getWidth() , child->getRect().getHeight() + HEADER_HEIGHT );
+ mExpandedHeight = getRect().getHeight();
+ }
+
+ bool res = LLUICtrl::addChild(child, tab_group);
+
+ if(DD_HEADER_NAME != child->getName())
+ {
+ if(!mCollapsible)
+ setDisplayChildren(true);
+ else
+ setDisplayChildren(getDisplayChildren());
+ }
+
+ if (!mContainerPanel)
+ mContainerPanel = findContainerView();
+
+ return res;
+}
+
+void LLAccordionCtrlTab::setAccordionView(LLView* panel)
+{
+ addChild(panel,0);
+}
+
+std::string LLAccordionCtrlTab::getTitle() const
+{
+ LLAccordionCtrlTabHeader* header = findChild<LLAccordionCtrlTabHeader>(DD_HEADER_NAME);
+ if (header)
+ {
+ return header->getTitle();
+ }
+ else
+ {
+ return LLStringUtil::null;
+ }
+}
+
+void LLAccordionCtrlTab::setTitle(const std::string& title, const std::string& hl)
+{
+ LLAccordionCtrlTabHeader* header = findChild<LLAccordionCtrlTabHeader>(DD_HEADER_NAME);
+ if (header)
+ {
+ header->setTitle(title, hl);
+ }
+}
+
+void LLAccordionCtrlTab::setTitleFontStyle(std::string style)
+{
+ LLAccordionCtrlTabHeader* header = findChild<LLAccordionCtrlTabHeader>(DD_HEADER_NAME);
+ if (header)
+ {
+ header->setTitleFontStyle(style);
+ }
+}
+
+void LLAccordionCtrlTab::setTitleColor(LLUIColor color)
+{
+ LLAccordionCtrlTabHeader* header = findChild<LLAccordionCtrlTabHeader>(DD_HEADER_NAME);
+ if (header)
+ {
+ header->setTitleColor(color);
+ }
+}
+
+boost::signals2::connection LLAccordionCtrlTab::setFocusReceivedCallback(const focus_signal_t::slot_type& cb)
+{
+ LLAccordionCtrlTabHeader* header = findChild<LLAccordionCtrlTabHeader>(DD_HEADER_NAME);
+ if (header)
+ {
+ return header->setFocusReceivedCallback(cb);
+ }
+ return boost::signals2::connection();
+}
+
+boost::signals2::connection LLAccordionCtrlTab::setFocusLostCallback(const focus_signal_t::slot_type& cb)
+{
+ LLAccordionCtrlTabHeader* header = findChild<LLAccordionCtrlTabHeader>(DD_HEADER_NAME);
+ if (header)
+ {
+ return header->setFocusLostCallback(cb);
+ }
+ return boost::signals2::connection();
+}
+
+void LLAccordionCtrlTab::setSelected(bool is_selected)
+{
+ LLAccordionCtrlTabHeader* header = findChild<LLAccordionCtrlTabHeader>(DD_HEADER_NAME);
+ if (header)
+ {
+ header->setSelected(is_selected);
+ }
+}
+
+LLView* LLAccordionCtrlTab::findContainerView()
+{
+ 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;
+ return child;
+ }
+ return NULL;
+}
+
+void LLAccordionCtrlTab::selectOnFocusReceived()
+{
+ if (getParent()) // A parent may not be set if tabs are added dynamically.
+ getParent()->notifyParent(LLSD().with("action", "select_current"));
+}
+
+void LLAccordionCtrlTab::deselectOnFocusLost()
+{
+ if(getParent()) // A parent may not be set if tabs are added dynamically.
+ {
+ getParent()->notifyParent(LLSD().with("action", "deselect_current"));
+ }
+
+}
+
+S32 LLAccordionCtrlTab::getHeaderHeight()
+{
+ return mHeaderVisible?HEADER_HEIGHT:0;
+}
+
+void LLAccordionCtrlTab::setHeaderVisible(bool value)
+{
+ if(mHeaderVisible == value)
+ return;
+ mHeaderVisible = value;
+ if(mHeader)
+ mHeader->setVisible(value);
+ reshape(getRect().getWidth(), getRect().getHeight(), FALSE);
+};
+
+//virtual
+BOOL LLAccordionCtrlTab::postBuild()
+{
+ if(mHeader)
+ mHeader->setVisible(mHeaderVisible);
+
+ static LLUICachedControl<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)
+{
+ if(info.has("action"))
+ {
+ std::string str_action = info["action"];
+ if(str_action == "store_state")
+ {
+ storeOpenCloseState();
+ return true;
+ }
+ if(str_action == "restore_state")
+ {
+ restoreOpenCloseState();
+ return true;
+ }
+ }
+ return LLUICtrl::notifyChildren(info);
+}
+
+S32 LLAccordionCtrlTab::notifyParent(const LLSD& info)
+{
+ if(info.has("action"))
+ {
+ std::string str_action = info["action"];
+ if(str_action == "size_changes")
+ {
+ //
+ S32 height = info["height"];
+ height = llmax(height,10) + HEADER_HEIGHT + getPaddingTop() + getPaddingBottom();
+
+ mExpandedHeight = height;
+
+ if(isExpanded())
+ {
+ LLRect panel_rect = getRect();
+ panel_rect.setLeftTopAndSize( panel_rect.mLeft, panel_rect.mTop, panel_rect.getWidth(), height);
+ reshape(getRect().getWidth(),height);
+ setRect(panel_rect);
+ }
+
+ //LLAccordionCtrl should rearrange accordion tab if one of accordion change its size
+ if (getParent()) // A parent may not be set if tabs are added dynamically.
+ getParent()->notifyParent(info);
+ return 1;
+ }
+ else if(str_action == "select_prev")
+ {
+ showAndFocusHeader();
+ return 1;
+ }
+ }
+ else if (info.has("scrollToShowRect"))
+ {
+ LLAccordionCtrl* parent = dynamic_cast<LLAccordionCtrl*>(getParent());
+ if (parent && parent->getFitParent())
+ {
+ // EXT-8285 ('No attachments worn' text appears at the bottom of blank 'Attachments' accordion)
+ // The problem was in passing message "scrollToShowRect" IN LLAccordionCtrlTab::notifyParent
+ // FROM child LLScrollContainer TO parent LLAccordionCtrl with "it_parent" set to true.
+
+ // It is wrong notification for parent accordion which leads to recursive call of adjustContainerPanel
+ // As the result of recursive call of adjustContainerPanel we got LLAccordionCtrlTab
+ // that reshaped and re-sized with different rectangles.
+
+ // LLAccordionCtrl has own scrollContainer and LLAccordionCtrlTab has own scrollContainer
+ // both should handle own scroll container's event.
+ // So, if parent accordion "fit_parent" accordion tab should handle its scroll container events itself.
+
+ return 1;
+ }
+ }
+
+ return LLUICtrl::notifyParent(info);
+}
+
+S32 LLAccordionCtrlTab::notify(const LLSD& info)
+{
+ if(info.has("action"))
+ {
+ std::string str_action = info["action"];
+ if(str_action == "select_first")
+ {
+ showAndFocusHeader();
+ return 1;
+ }
+ else if( str_action == "select_last" )
+ {
+ if(getDisplayChildren() == false)
+ {
+ showAndFocusHeader();
+ }
+ else
+ {
+ LLView* view = getAccordionView();
+ if(view)
+ view->notify(LLSD().with("action","select_last"));
+ }
+ }
+ }
+ return 0;
+}
+
+BOOL LLAccordionCtrlTab::handleKey(KEY key, MASK mask, BOOL called_from_parent)
+{
+ LLAccordionCtrlTabHeader* header = getChild<LLAccordionCtrlTabHeader>(DD_HEADER_NAME);
+ 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)
+ {
+ changeOpenClose(getDisplayChildren());
+ return TRUE;
+ }
+ }
+ if ( (key == KEY_SUBTRACT || key == KEY_LEFT)&& mask == MASK_NONE)
+ {
+ if(getDisplayChildren() == true)
+ {
+ changeOpenClose(getDisplayChildren());
+ return TRUE;
+ }
+ }
+
+ if ( key == KEY_DOWN && mask == MASK_NONE)
+ {
+ //if collapsed go to the next accordion
+ if(getDisplayChildren() == false)
+ //we processing notifyParent so let call parent directly
+ getParent()->notifyParent(LLSD().with("action","select_next"));
+ else
+ {
+ getAccordionView()->notify(LLSD().with("action","select_first"));
+ }
+ return TRUE;
+ }
+
+ if ( key == KEY_UP && mask == MASK_NONE)
+ {
+ //go to the previous accordion
+
+ //we processing notifyParent so let call parent directly
+ getParent()->notifyParent(LLSD().with("action","select_prev"));
+ return TRUE;
+ }
+
+ return LLUICtrl::handleKey(key, mask, called_from_parent);
+}
+
+void LLAccordionCtrlTab::showAndFocusHeader()
+{
+ LLAccordionCtrlTabHeader* header = getChild<LLAccordionCtrlTabHeader>(DD_HEADER_NAME);
+ header->setFocus(true);
+ header->setSelected(mSelectionEnabled);
+
+ LLRect screen_rc;
+ LLRect selected_rc = header->getRect();
+ localRectToScreen(selected_rc, &screen_rc);
+ notifyParent(LLSD().with("scrollToShowRect",screen_rc.getValue()));
+
+}
+void LLAccordionCtrlTab::storeOpenCloseState()
+{
+ if(mWasStateStored)
+ return;
+ mStoredOpenCloseState = getDisplayChildren();
+ mWasStateStored = true;
+}
+
+void LLAccordionCtrlTab::restoreOpenCloseState()
+{
+ if(!mWasStateStored)
+ return;
+ if(getDisplayChildren() != mStoredOpenCloseState)
+ {
+ changeOpenClose(getDisplayChildren());
+ }
+ mWasStateStored = false;
+}
+
+void LLAccordionCtrlTab::adjustContainerPanel ()
+{
+ S32 width = getRect().getWidth();
+ S32 height = getRect().getHeight();
+
+ LLRect child_rect;
+ child_rect.setLeftTopAndSize(
+ getPaddingLeft(),
+ height - getHeaderHeight() - getPaddingTop(),
+ width - getPaddingLeft() - getPaddingRight(),
+ height - getHeaderHeight() - getPaddingTop() - getPaddingBottom() );
+
+ adjustContainerPanel(child_rect);
+}
+
+void LLAccordionCtrlTab::adjustContainerPanel(const LLRect& child_rect)
+{
+ if(!mContainerPanel)
+ return;
+
+ if(!mFitPanel)
+ {
+ show_hide_scrollbar(child_rect);
+ updateLayout(child_rect);
+ }
+ else
+ {
+ mContainerPanel->reshape(child_rect.getWidth(),child_rect.getHeight());
+ mContainerPanel->setRect(child_rect);
+ }
+}
+
+S32 LLAccordionCtrlTab::getChildViewHeight()
+{
+ if(!mContainerPanel)
+ return 0;
+ return mContainerPanel->getRect().getHeight();
+}
+
+void LLAccordionCtrlTab::show_hide_scrollbar(const LLRect& child_rect)
+{
+ if(getChildViewHeight() > child_rect.getHeight() )
+ showScrollbar(child_rect);
+ else
+ hideScrollbar(child_rect);
+}
+void LLAccordionCtrlTab::showScrollbar(const LLRect& child_rect)
+{
+ if(!mContainerPanel || !mScrollbar)
+ return;
+ bool was_visible = mScrollbar->getVisible();
+ mScrollbar->setVisible(true);
+
+ static LLUICachedControl<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
+ mHeader->handleToolTip(x, y, mask);
+ return TRUE;
+ }
+ return LLUICtrl::handleToolTip(x, y, mask);
+}
+BOOL LLAccordionCtrlTab::handleScrollWheel ( S32 x, S32 y, S32 clicks )
+{
+ if( LLUICtrl::handleScrollWheel(x,y,clicks))
+ {
+ return TRUE;
+ }
+ if( mScrollbar && mScrollbar->getVisible() && mScrollbar->handleScrollWheel( 0, 0, clicks ) )
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
diff --git a/indra/llui/llaccordionctrltab.h b/indra/llui/llaccordionctrltab.h
new file mode 100644
index 0000000000..d6ac8cbc8f
--- /dev/null
+++ b/indra/llui/llaccordionctrltab.h
@@ -0,0 +1,247 @@
+/**
+ * @file LLAccordionCtrlTab.h
+ * @brief Collapsible box control implementation
+ *
+ * $LicenseInfo:firstyear=2004&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_ACCORDIONCTRLTAB_H_
+#define LL_ACCORDIONCTRLTAB_H_
+
+#include <string>
+#include "llrect.h"
+#include "lluictrl.h"
+#include "lluicolor.h"
+#include "llstyle.h"
+
+class LLUICtrlFactory;
+class LLUIImage;
+class LLButton;
+class LLTextBox;
+class LLScrollbar;
+
+
+
+// LLAccordionCtrlTab is a container for other controls.
+// It has a Header, by clicking on which hosted controls are shown or hidden.
+// When hosted controls are show - LLAccordionCtrlTab is expanded.
+// When hosted controls are hidden - LLAccordionCtrlTab is collapsed.
+
+class LLAccordionCtrlTab : public LLUICtrl
+{
+// Interface
+public:
+
+ struct Params
+ : public LLInitParam::Block<Params, LLUICtrl::Params>
+ {
+ Optional<bool> display_children, //expanded or collapsed after initialization
+ collapsible;
+
+ Optional<std::string> title;
+
+ Optional<S32> header_height,
+ min_width,
+ min_height;
+
+ // Overlay images (arrows on the left)
+ Mandatory<LLUIImage*> header_expand_img,
+ header_expand_img_pressed,
+ header_collapse_img,
+ header_collapse_img_pressed;
+
+ // Background images for the accordion tabs
+ Mandatory<LLUIImage*> header_image,
+ header_image_over,
+ header_image_pressed,
+ header_image_focused;
+
+ Optional<LLUIColor> header_bg_color,
+ header_text_color,
+ dropdown_bg_color;
+
+ Optional<bool> header_visible;
+
+ Optional<bool> fit_panel;
+
+ Optional<bool> selection_enabled;
+
+ Optional<S32> padding_left;
+ Optional<S32> padding_right;
+ Optional<S32> padding_top;
+ Optional<S32> padding_bottom;
+
+ Params();
+ };
+
+ typedef LLDefaultChildRegistry child_registry_t;
+
+ virtual ~LLAccordionCtrlTab();
+
+ // Registers callback for expand/collapse events.
+ boost::signals2::connection setDropDownStateChangedCallback(commit_callback_t cb);
+
+ // Changes expand/collapse state
+ virtual void setDisplayChildren(bool display);
+
+ // Returns expand/collapse state
+ virtual bool getDisplayChildren() const {return mDisplayChildren;};
+
+ //set LLAccordionCtrlTab panel
+ void setAccordionView(LLView* panel);
+ LLView* getAccordionView() { return mContainerPanel; };
+
+ std::string getTitle() const;
+
+ // Set text and highlight substring in LLAccordionCtrlTabHeader
+ void setTitle(const std::string& title, const std::string& hl = LLStringUtil::null);
+
+ // Set text font style in LLAccordionCtrlTabHeader
+ void setTitleFontStyle(std::string style);
+
+ // Set text color in LLAccordionCtrlTabHeader
+ void setTitleColor(LLUIColor color);
+
+ boost::signals2::connection setFocusReceivedCallback(const focus_signal_t::slot_type& cb);
+ boost::signals2::connection setFocusLostCallback(const focus_signal_t::slot_type& cb);
+
+ void setSelected(bool is_selected);
+
+ bool getCollapsible() {return mCollapsible;};
+
+ void setCollapsible(bool collapsible) {mCollapsible = collapsible;};
+ void changeOpenClose(bool is_open);
+
+ void canOpenClose(bool can_open_close) { mCanOpenClose = can_open_close;};
+ bool canOpenClose() const { return mCanOpenClose; };
+
+ virtual BOOL postBuild();
+
+ S32 notifyParent(const LLSD& info);
+ S32 notify(const LLSD& info);
+ bool notifyChildren(const LLSD& info);
+
+ void draw();
+
+ void storeOpenCloseState ();
+ void restoreOpenCloseState ();
+
+protected:
+ LLAccordionCtrlTab(const LLAccordionCtrlTab::Params&);
+ friend class LLUICtrlFactory;
+
+// Overrides
+public:
+
+ // Call reshape after changing size
+ virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
+
+ /**
+ * Raises notifyParent event with "child_visibility_change" = new_visibility
+ */
+ void handleVisibilityChange(BOOL new_visibility);
+
+ // Changes expand/collapse state and triggers expand/collapse callbacks
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ virtual BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent);
+
+ virtual BOOL handleToolTip(S32 x, S32 y, MASK mask);
+ virtual BOOL handleScrollWheel( S32 x, S32 y, S32 clicks );
+
+
+ virtual bool addChild(LLView* child, S32 tab_group);
+
+ bool isExpanded() const { return mDisplayChildren; }
+
+ S32 getHeaderHeight();
+
+ // Min size functions
+
+ void setHeaderVisible(bool value);
+
+ bool getHeaderVisible() { return mHeaderVisible;}
+
+ S32 mExpandedHeight; // Height of expanded ctrl.
+ // Used to restore height after expand.
+
+ S32 getPaddingLeft() const { return mPaddingLeft;}
+ S32 getPaddingRight() const { return mPaddingRight;}
+ S32 getPaddingTop() const { return mPaddingTop;}
+ S32 getPaddingBottom() const { return mPaddingBottom;}
+
+ void showAndFocusHeader();
+
+ void setFitPanel( bool fit ) { mFitPanel = true; }
+ bool getFitParent() const { return mFitPanel; }
+
+protected:
+ void adjustContainerPanel (const LLRect& child_rect);
+ void adjustContainerPanel ();
+ S32 getChildViewHeight ();
+
+ void onScrollPosChangeCallback(S32, LLScrollbar*);
+
+ void show_hide_scrollbar (const LLRect& child_rect);
+ void showScrollbar (const LLRect& child_rect);
+ void hideScrollbar (const LLRect& child_rect);
+
+ void updateLayout ( const LLRect& child_rect );
+ void ctrlSetLeftTopAndSize (LLView* panel, S32 left, S32 top, S32 width, S32 height);
+
+ void drawChild(const LLRect& root_rect,LLView* child);
+
+ LLView* findContainerView ();
+
+ void selectOnFocusReceived();
+ void deselectOnFocusLost();
+
+private:
+
+ class LLAccordionCtrlTabHeader;
+ LLAccordionCtrlTabHeader* mHeader; //Header
+
+ bool mDisplayChildren; //Expanded/collapsed
+ bool mCollapsible;
+ bool mHeaderVisible;
+
+ bool mCanOpenClose;
+ bool mFitPanel;
+
+ S32 mPaddingLeft;
+ S32 mPaddingRight;
+ S32 mPaddingTop;
+ S32 mPaddingBottom;
+
+ bool mStoredOpenCloseState;
+ bool mWasStateStored;
+
+ bool mSelectionEnabled;
+
+ LLScrollbar* mScrollbar;
+ LLView* mContainerPanel;
+
+ LLUIColor mDropdownBGColor;
+};
+
+#endif
diff --git a/indra/llui/llbutton.cpp b/indra/llui/llbutton.cpp
index 110ad82763..d51276bf26 100644
--- a/indra/llui/llbutton.cpp
+++ b/indra/llui/llbutton.cpp
@@ -1,38 +1,33 @@
+
/**
* @file llbutton.cpp
* @brief LLButton base class
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "linden_common.h"
-#define INSTANTIATE_GETCHILD_BUTTON
+#define LLBUTTON_CPP
#include "llbutton.h"
// Linden library includes
@@ -40,7 +35,6 @@
#include "llstring.h"
// Project includes
-#include "llhtmlhelp.h"
#include "llkeyboard.h"
#include "llui.h"
#include "lluiconstants.h"
@@ -50,31 +44,41 @@
#include "llfloaterreg.h"
#include "llfocusmgr.h"
#include "llwindow.h"
+#include "llnotificationsutil.h"
#include "llrender.h"
#include "lluictrlfactory.h"
+#include "llhelp.h"
+#include "lldockablefloater.h"
-static LLDefaultWidgetRegistry::Register<LLButton> r("button");
+static LLDefaultChildRegistry::Register<LLButton> r("button");
+
+// Compiler optimization, generate extern template
+template class LLButton* LLView::getChild<class LLButton>(
+ const std::string& name, BOOL recurse) const;
// globals loaded from settings.xml
S32 LLBUTTON_H_PAD = 0;
-S32 LLBUTTON_V_PAD = 0;
S32 BTN_HEIGHT_SMALL= 0;
S32 BTN_HEIGHT = 0;
-template LLButton* LLView::getChild<LLButton>( const std::string& name, BOOL recurse, BOOL create_if_missing ) const;
-
LLButton::Params::Params()
: label_selected("label_selected"), // requires is_toggle true
- label_dropshadow("label_shadow", true),
+ label_shadow("label_shadow", true),
auto_resize("auto_resize", false),
+ use_ellipses("use_ellipses", false),
image_unselected("image_unselected"),
image_selected("image_selected"),
image_hover_selected("image_hover_selected"),
image_hover_unselected("image_hover_unselected"),
image_disabled_selected("image_disabled_selected"),
image_disabled("image_disabled"),
+ image_pressed("image_pressed"),
+ image_pressed_selected("image_pressed_selected"),
image_overlay("image_overlay"),
image_overlay_alignment("image_overlay_alignment", std::string("center")),
+ image_top_pad("image_top_pad"),
+ image_bottom_pad("image_bottom_pad"),
+ imgoverlay_label_space("imgoverlay_label_space", 1),
label_color("label_color"),
label_color_selected("label_color_selected"), // requires is_toggle true
label_color_disabled("label_color_disabled"),
@@ -86,16 +90,15 @@ LLButton::Params::Params()
flash_color("flash_color"),
pad_right("pad_right", LLUI::sSettingGroups["config"]->getS32("ButtonHPad")),
pad_left("pad_left", LLUI::sSettingGroups["config"]->getS32("ButtonHPad")),
+ pad_bottom("pad_bottom"),
click_callback("click_callback"),
mouse_down_callback("mouse_down_callback"),
mouse_up_callback("mouse_up_callback"),
mouse_held_callback("mouse_held_callback"),
is_toggle("is_toggle", false),
scale_image("scale_image", true),
- help_url("help_url"),
hover_glow_amount("hover_glow_amount"),
- commit_on_return("commit_on_return", true),
- picture_style("picture_style", false)
+ commit_on_return("commit_on_return", true)
{
addSynonym(is_toggle, "toggle");
held_down_delay.seconds = 0.5f;
@@ -111,7 +114,7 @@ LLButton::LLButton(const LLButton::Params& p)
mFlashing( FALSE ),
mCurGlowStrength(0.f),
mNeedsHighlight(FALSE),
- mImagep( NULL ),
+ mMouseOver(false),
mUnselectedLabel(p.label()),
mSelectedLabel(p.label_selected()),
mGLFont(p.font),
@@ -121,6 +124,9 @@ LLButton::LLButton(const LLButton::Params& p)
mImageSelected(p.image_selected),
mImageDisabled(p.image_disabled),
mImageDisabledSelected(p.image_disabled_selected),
+ mImageFlash(p.image_flash),
+ mImagePressed(p.image_pressed),
+ mImagePressedSelected(p.image_pressed_selected),
mImageHoverSelected(p.image_hover_selected),
mImageHoverUnselected(p.image_hover_unselected),
mUnselectedLabelColor(p.label_color()),
@@ -134,31 +140,34 @@ 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_dropshadow),
+ mDropShadowedText(p.label_shadow),
mAutoResize(p.auto_resize),
+ mUseEllipses( p.use_ellipses ),
mHAlign(p.font_halign),
mLeftHPad(p.pad_left),
mRightHPad(p.pad_right),
+ mBottomVPad(p.pad_bottom),
mHoverGlowStrength(p.hover_glow_amount),
mCommitOnReturn(p.commit_on_return),
- mFadeWhenDisabled(FALSE)
+ mFadeWhenDisabled(FALSE),
+ mForcePressedState(false),
+ mLastDrawCharsCount(0),
+ mMouseDownSignal(NULL),
+ mMouseUpSignal(NULL),
+ mHeldDownSignal(NULL)
+
{
static LLUICachedControl<S32> llbutton_orig_h_pad ("UIButtonOrigHPad", 0);
- static LLButton::Params default_params(LLUICtrlFactory::getDefaultParams<LLButton::Params>());
+ static Params default_params(LLUICtrlFactory::getDefaultParams<LLButton>());
- //if we aren't a picture_style button set label as name if not provided
- if (!p.picture_style.isProvided() || !p.picture_style)
+ if (!p.label_selected.isProvided())
{
- if (!p.label.isProvided())
- {
- mUnselectedLabel = p.name();
- }
- if (!p.label_selected.isProvided())
- {
- mSelectedLabel = mUnselectedLabel.getString();
- }
+ mSelectedLabel = mUnselectedLabel;
}
// Hack to make sure there is space for at least one character
@@ -171,11 +180,6 @@ LLButton::LLButton(const LLButton::Params& p)
mMouseDownTimer.stop();
- if (p.help_url.isProvided())
- {
- setHelpURLCallback(p.help_url);
- }
-
// if custom unselected button image provided...
if (p.image_unselected != default_params.image_unselected)
{
@@ -185,6 +189,11 @@ LLButton::LLButton(const LLButton::Params& p)
mImageDisabled = p.image_unselected;
mFadeWhenDisabled = TRUE;
}
+
+ if (p.image_pressed_selected == default_params.image_pressed_selected)
+ {
+ mImagePressedSelected = mImageUnselected;
+ }
}
// if custom selected button image provided...
@@ -196,6 +205,21 @@ LLButton::LLButton(const LLButton::Params& p)
mImageDisabledSelected = p.image_selected;
mFadeWhenDisabled = TRUE;
}
+
+ if (p.image_pressed == default_params.image_pressed)
+ {
+ mImagePressed = mImageSelected;
+ }
+ }
+
+ if (!p.image_pressed.isProvided())
+ {
+ mImagePressed = mImageSelected;
+ }
+
+ if (!p.image_pressed_selected.isProvided())
+ {
+ mImagePressedSelected = mImageUnselected;
}
if (mImageUnselected.isNull())
@@ -204,13 +228,28 @@ LLButton::LLButton(const LLButton::Params& p)
}
if (p.click_callback.isProvided())
- initCommitCallback(p.click_callback, mCommitSignal); // alias -> commit_callback
+ {
+ setCommitCallback(initCommitCallback(p.click_callback)); // alias -> commit_callback
+ }
if (p.mouse_down_callback.isProvided())
- initCommitCallback(p.mouse_down_callback, mMouseDownSignal);
+ {
+ setMouseDownCallback(initCommitCallback(p.mouse_down_callback));
+ }
if (p.mouse_up_callback.isProvided())
- initCommitCallback(p.mouse_up_callback, mMouseUpSignal);
+ {
+ setMouseUpCallback(initCommitCallback(p.mouse_up_callback));
+ }
if (p.mouse_held_callback.isProvided())
- initCommitCallback(p.mouse_held_callback, mHeldDownSignal);
+ {
+ setHeldDownCallback(initCommitCallback(p.mouse_held_callback));
+ }
+}
+
+LLButton::~LLButton()
+{
+ delete mMouseDownSignal;
+ delete mMouseUpSignal;
+ delete mHeldDownSignal;
}
// HACK: Committing a button is the same as instantly clicking it.
@@ -221,9 +260,9 @@ void LLButton::onCommit()
// panel containing it. Therefore we need to call LLUICtrl::onCommit()
// LAST, otherwise this becomes deleted memory.
- mMouseDownSignal(this, LLSD());
+ if (mMouseDownSignal) (*mMouseDownSignal)(this, LLSD());
- mMouseUpSignal(this, LLSD());
+ if (mMouseUpSignal) (*mMouseUpSignal)(this, LLSD());
if (getSoundFlags() & MOUSE_DOWN)
{
@@ -244,43 +283,42 @@ void LLButton::onCommit()
LLUICtrl::onCommit();
}
-boost::signals::connection LLButton::setClickedCallback( const commit_signal_t::slot_type& cb )
+boost::signals2::connection LLButton::setClickedCallback( const commit_signal_t::slot_type& cb )
{
- return mCommitSignal.connect(cb);
+ if (!mCommitSignal) mCommitSignal = new commit_signal_t();
+ return mCommitSignal->connect(cb);
}
-boost::signals::connection LLButton::setMouseDownCallback( const commit_signal_t::slot_type& cb )
+boost::signals2::connection LLButton::setMouseDownCallback( const commit_signal_t::slot_type& cb )
{
- return mMouseDownSignal.connect(cb);
+ if (!mMouseDownSignal) mMouseDownSignal = new commit_signal_t();
+ return mMouseDownSignal->connect(cb);
}
-boost::signals::connection LLButton::setMouseUpCallback( const commit_signal_t::slot_type& cb )
+boost::signals2::connection LLButton::setMouseUpCallback( const commit_signal_t::slot_type& cb )
{
- return mMouseUpSignal.connect(cb);
+ if (!mMouseUpSignal) mMouseUpSignal = new commit_signal_t();
+ return mMouseUpSignal->connect(cb);
}
-boost::signals::connection LLButton::setHeldDownCallback( const commit_signal_t::slot_type& cb )
+boost::signals2::connection LLButton::setHeldDownCallback( const commit_signal_t::slot_type& cb )
{
- return mHeldDownSignal.connect(cb);
-}
-
-boost::signals::connection LLButton::setRightClickedCallback( const commit_signal_t::slot_type& cb )
-{
- return mRightClickSignal.connect(cb);
+ if (!mHeldDownSignal) mHeldDownSignal = new commit_signal_t();
+ return mHeldDownSignal->connect(cb);
}
// *TODO: Deprecate (for backwards compatability only)
-boost::signals::connection LLButton::setClickedCallback( button_callback_t cb, void* data )
+boost::signals2::connection LLButton::setClickedCallback( button_callback_t cb, void* data )
{
return setClickedCallback(boost::bind(cb, data));
}
-boost::signals::connection LLButton::setMouseDownCallback( button_callback_t cb, void* data )
+boost::signals2::connection LLButton::setMouseDownCallback( button_callback_t cb, void* data )
{
return setMouseDownCallback(boost::bind(cb, data));
}
-boost::signals::connection LLButton::setMouseUpCallback( button_callback_t cb, void* data )
+boost::signals2::connection LLButton::setMouseUpCallback( button_callback_t cb, void* data )
{
return setMouseUpCallback(boost::bind(cb, data));
}
-boost::signals::connection LLButton::setHeldDownCallback( button_callback_t cb, void* data )
+boost::signals2::connection LLButton::setHeldDownCallback( button_callback_t cb, void* data )
{
return setHeldDownCallback(boost::bind(cb, data));
}
@@ -328,25 +366,35 @@ BOOL LLButton::handleKeyHere(KEY key, MASK mask )
BOOL LLButton::handleMouseDown(S32 x, S32 y, MASK mask)
{
- // Route future Mouse messages here preemptively. (Release on mouse up.)
- gFocusMgr.setMouseCapture( this );
-
- if (hasTabStop() && !getIsChrome())
+ if (!childrenHandleMouseDown(x, y, mask))
{
- setFocus(TRUE);
- }
+ // Route future Mouse messages here preemptively. (Release on mouse up.)
+ gFocusMgr.setMouseCapture( this );
- mMouseDownSignal(this, LLSD());
+ if (hasTabStop() && !getIsChrome())
+ {
+ setFocus(TRUE);
+ }
- mMouseDownTimer.start();
- mMouseDownFrame = (S32) LLFrameTimer::getFrameCount();
- mMouseHeldDownCount = 0;
-
- if (getSoundFlags() & MOUSE_DOWN)
- {
- make_ui_sound("UISndClick");
- }
+ /*
+ * ATTENTION! This call fires another mouse down callback.
+ * If you wish to remove this call emit that signal directly
+ * by calling LLUICtrl::mMouseDownSignal(x, y, mask);
+ */
+ LLUICtrl::handleMouseDown(x, y, mask);
+
+ if(mMouseDownSignal) (*mMouseDownSignal)(this, LLSD());
+
+ mMouseDownTimer.start();
+ mMouseDownFrame = (S32) LLFrameTimer::getFrameCount();
+ mMouseHeldDownCount = 0;
+
+ if (getSoundFlags() & MOUSE_DOWN)
+ {
+ make_ui_sound("UISndClick");
+ }
+ }
return TRUE;
}
@@ -359,8 +407,15 @@ BOOL LLButton::handleMouseUp(S32 x, S32 y, MASK mask)
// Always release the mouse
gFocusMgr.setMouseCapture( NULL );
+ /*
+ * ATTENTION! This call fires another mouse up callback.
+ * If you wish to remove this call emit that signal directly
+ * by calling LLUICtrl::mMouseUpSignal(x, y, mask);
+ */
+ LLUICtrl::handleMouseUp(x, y, mask);
+
// Regardless of where mouseup occurs, handle callback
- mMouseUpSignal(this, LLSD());
+ if(mMouseUpSignal) (*mMouseUpSignal)(this, LLSD());
resetMouseDownTimer();
@@ -381,21 +436,35 @@ BOOL LLButton::handleMouseUp(S32 x, S32 y, MASK mask)
LLUICtrl::onCommit();
}
}
+ else
+ {
+ childrenHandleMouseUp(x, y, mask);
+ }
return TRUE;
}
BOOL LLButton::handleRightMouseDown(S32 x, S32 y, MASK mask)
{
- // Route future Mouse messages here preemptively. (Release on mouse up.)
- gFocusMgr.setMouseCapture( this );
-
- if (hasTabStop() && !getIsChrome())
+ if (!childrenHandleRightMouseDown(x, y, mask))
{
- setFocus(TRUE);
- }
+ // Route future Mouse messages here preemptively. (Release on mouse up.)
+ gFocusMgr.setMouseCapture( this );
+ if (hasTabStop() && !getIsChrome())
+ {
+ setFocus(TRUE);
+ }
+// if (pointInView(x, y))
+// {
+// }
+ }
+ // send the mouse down signal
+ LLUICtrl::handleRightMouseDown(x,y,mask);
+ // *TODO: Return result of LLUICtrl call above? Should defer to base class
+ // but this might change the mouse handling of existing buttons in a bad way
+ // if they are not mouse opaque.
return TRUE;
}
@@ -407,43 +476,68 @@ BOOL LLButton::handleRightMouseUp(S32 x, S32 y, MASK mask)
// Always release the mouse
gFocusMgr.setMouseCapture( NULL );
- if (pointInView(x, y))
- {
- mRightClickSignal(this, getValue());
- }
+// if (pointInView(x, y))
+// {
+// mRightMouseUpSignal(this, x,y,mask);
+// }
}
+ else
+ {
+ childrenHandleRightMouseUp(x, y, mask);
+ }
+ // send the mouse up signal
+ LLUICtrl::handleRightMouseUp(x,y,mask);
+ // *TODO: Return result of LLUICtrl call above? Should defer to base class
+ // but this might change the mouse handling of existing buttons in a bad way.
+ // if they are not mouse opaque.
return TRUE;
}
void LLButton::onMouseEnter(S32 x, S32 y, MASK mask)
{
- if (getEnabled())
+ LLUICtrl::onMouseEnter(x, y, mask);
+
+ if (isInEnabledChain())
+ {
mNeedsHighlight = TRUE;
+ }
+
+ mMouseOver = true;
}
void LLButton::onMouseLeave(S32 x, S32 y, MASK mask)
{
+ LLUICtrl::onMouseLeave(x, y, mask);
+
mNeedsHighlight = FALSE;
+ mMouseOver = true;
+}
+
+void LLButton::setHighlight(bool b)
+{
+ mNeedsHighlight = b;
}
BOOL LLButton::handleHover(S32 x, S32 y, MASK mask)
{
- if (mMouseDownTimer.getStarted())
+ if (!childrenHandleHover(x, y, mask))
{
- F32 elapsed = getHeldDownTime();
- if( mHeldDownDelay <= elapsed && mHeldDownFrameDelay <= (S32)LLFrameTimer::getFrameCount() - mMouseDownFrame)
+ if (mMouseDownTimer.getStarted())
{
- LLSD param;
- param["count"] = mMouseHeldDownCount++;
- mHeldDownSignal(this, param);
+ F32 elapsed = getHeldDownTime();
+ if( mHeldDownDelay <= elapsed && mHeldDownFrameDelay <= (S32)LLFrameTimer::getFrameCount() - mMouseDownFrame)
+ {
+ LLSD param;
+ param["count"] = mMouseHeldDownCount++;
+ if (mHeldDownSignal) (*mHeldDownSignal)(this, param);
+ }
}
- }
-
- // We only handle the click if the click both started and ended within us
- getWindow()->setCursor(UI_CURSOR_ARROW);
- lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << llendl;
+ // We only handle the click if the click both started and ended within us
+ getWindow()->setCursor(UI_CURSOR_ARROW);
+ lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << llendl;
+ }
return TRUE;
}
@@ -451,7 +545,8 @@ BOOL LLButton::handleHover(S32 x, S32 y, MASK mask)
// virtual
void LLButton::draw()
{
- BOOL flash = FALSE;
+ F32 alpha = getDrawContext().mAlpha;
+ bool flash = FALSE;
static LLUICachedControl<F32> button_flash_rate("ButtonFlashRate", 0);
static LLUICachedControl<S32> button_flash_count("ButtonFlashCount", 0);
@@ -463,35 +558,39 @@ void LLButton::draw()
flash = (flash_count % 2 == 0) || flash_count > S32((F32)button_flash_count * 2.f);
}
- BOOL pressed_by_keyboard = FALSE;
+ bool pressed_by_keyboard = FALSE;
if (hasFocus())
{
pressed_by_keyboard = gKeyboard->getKeyDown(' ') || (mCommitOnReturn && gKeyboard->getKeyDown(KEY_RETURN));
}
// Unselected image assignments
- S32 local_mouse_x;
- S32 local_mouse_y;
- LLUI::getCursorPositionLocal(this, &local_mouse_x, &local_mouse_y);
+ bool enabled = isInEnabledChain();
- BOOL pressed = pressed_by_keyboard
- || (hasMouseCapture() && pointInView(local_mouse_x, local_mouse_y))
- || getToggleState();
+ bool pressed = pressed_by_keyboard
+ || (hasMouseCapture() && mMouseOver)
+ || mForcePressedState;
+ bool selected = getToggleState();
- BOOL use_glow_effect = FALSE;
+ bool use_glow_effect = FALSE;
LLColor4 glow_color = LLColor4::white;
LLRender::eBlendType glow_type = LLRender::BT_ADD_WITH_ALPHA;
- if ( mNeedsHighlight )
+ LLUIImage* imagep = NULL;
+ if (pressed)
{
- if (pressed)
+ imagep = selected ? mImagePressedSelected : mImagePressed;
+ }
+ else if ( mNeedsHighlight )
+ {
+ if (selected)
{
if (mImageHoverSelected)
{
- mImagep = mImageHoverSelected;
+ imagep = mImageHoverSelected;
}
else
{
- mImagep = mImageSelected;
+ imagep = mImageSelected;
use_glow_effect = TRUE;
}
}
@@ -499,34 +598,18 @@ void LLButton::draw()
{
if (mImageHoverUnselected)
{
- mImagep = mImageHoverUnselected;
+ imagep = mImageHoverUnselected;
}
else
{
- mImagep = mImageUnselected;
+ imagep = mImageUnselected;
use_glow_effect = TRUE;
}
}
}
- else if ( pressed )
- {
- mImagep = mImageSelected;
- }
- else
- {
- mImagep = mImageUnselected;
- }
-
- if (mFlashing)
+ else
{
- LLColor4 flash_color = mFlashBgColor.get();
- use_glow_effect = TRUE;
- glow_type = LLRender::BT_ALPHA; // blend the glow
-
- if (mNeedsHighlight) // highlighted AND flashing
- glow_color = (glow_color*0.5f + flash_color*0.5f) % 2.0f; // average between flash and highlight colour, with sum of the opacity
- else
- glow_color = flash_color;
+ imagep = selected ? mImageSelected : mImageUnselected;
}
// Override if more data is available
@@ -536,19 +619,41 @@ void LLButton::draw()
// disabled but checked
if (!mImageDisabledSelected.isNull()
&&
- ( (getEnabled() && getTentative())
- || (!getEnabled() && pressed ) ) )
+ ( (enabled && getTentative())
+ || (!enabled && selected ) ) )
{
- mImagep = mImageDisabledSelected;
+ imagep = mImageDisabledSelected;
}
else if (!mImageDisabled.isNull()
- && !getEnabled()
- && !pressed)
+ && !enabled
+ && !selected)
+ {
+ imagep = mImageDisabled;
+ }
+
+ if (mFlashing)
{
- mImagep = mImageDisabled;
+ // if button should flash and we have icon for flashing, use it as image for button
+ if(flash && mImageFlash)
+ {
+ // setting flash to false to avoid its further influence on glow
+ flash = false;
+ imagep = mImageFlash;
+ }
+ // else use usual flashing via flash_color
+ else
+ {
+ LLColor4 flash_color = mFlashBgColor.get();
+ use_glow_effect = TRUE;
+ glow_type = LLRender::BT_ALPHA; // blend the glow
+ if (mNeedsHighlight) // highlighted AND flashing
+ glow_color = (glow_color*0.5f + flash_color*0.5f) % 2.0f; // average between flash and highlight colour, with sum of the opacity
+ else
+ glow_color = flash_color;
+ }
}
- if (mNeedsHighlight && !mImagep)
+ if (mNeedsHighlight && !imagep)
{
use_glow_effect = TRUE;
}
@@ -557,7 +662,7 @@ void LLButton::draw()
LLColor4 label_color;
// label changes when button state changes, not when pressed
- if ( getEnabled() )
+ if ( enabled )
{
if ( getToggleState() )
{
@@ -585,32 +690,18 @@ void LLButton::draw()
if( getToggleState() )
{
- if( getEnabled() || mDisabledSelectedLabel.empty() )
- {
- label = mSelectedLabel;
- }
- else
- {
- label = mDisabledSelectedLabel;
- }
+ label = mSelectedLabel;
}
else
{
- if( getEnabled() || mDisabledLabel.empty() )
- {
- label = mUnselectedLabel;
- }
- else
- {
- label = mDisabledLabel;
- }
+ label = mUnselectedLabel;
}
// overlay with keyboard focus border
if (hasFocus())
{
F32 lerp_amt = gFocusMgr.getFocusFlashAmt();
- drawBorder(gFocusMgr.getFocusColor(), llround(lerp(1.f, 3.f, lerp_amt)));
+ drawBorder(imagep, gFocusMgr.getFocusColor() % alpha, llround(lerp(1.f, 3.f, lerp_amt)));
}
if (use_glow_effect)
@@ -627,27 +718,27 @@ void LLButton::draw()
// Draw button image, if available.
// Otherwise draw basic rectangular button.
- if (mImagep.notNull())
+ if (imagep != NULL)
{
// apply automatic 50% alpha fade to disabled image
LLColor4 disabled_color = mFadeWhenDisabled ? mDisabledImageColor.get() % 0.5f : mDisabledImageColor.get();
if ( mScaleImage)
{
- mImagep->draw(getLocalRect(), getEnabled() ? mImageColor.get() : disabled_color );
+ imagep->draw(getLocalRect(), (enabled ? mImageColor.get() : disabled_color) % alpha );
if (mCurGlowStrength > 0.01f)
{
gGL.setSceneBlendType(glow_type);
- mImagep->drawSolid(0, 0, getRect().getWidth(), getRect().getHeight(), glow_color % mCurGlowStrength);
+ imagep->drawSolid(0, 0, getRect().getWidth(), getRect().getHeight(), glow_color % (mCurGlowStrength * alpha));
gGL.setSceneBlendType(LLRender::BT_ALPHA);
}
}
else
{
- mImagep->draw(0, 0, getEnabled() ? mImageColor.get() : disabled_color );
+ imagep->draw(0, 0, (enabled ? mImageColor.get() : disabled_color) % alpha );
if (mCurGlowStrength > 0.01f)
{
gGL.setSceneBlendType(glow_type);
- mImagep->drawSolid(0, 0, glow_color % mCurGlowStrength);
+ imagep->drawSolid(0, 0, glow_color % (mCurGlowStrength * alpha));
gGL.setSceneBlendType(LLRender::BT_ALPHA);
}
}
@@ -657,7 +748,7 @@ void LLButton::draw()
// no image
lldebugs << "No image for button " << getName() << llendl;
// draw it in pink so we can find it
- gl_rect_2d(0, getRect().getHeight(), getRect().getWidth(), 0, LLColor4::pink1, FALSE);
+ gl_rect_2d(0, getRect().getHeight(), getRect().getWidth(), 0, LLColor4::pink1 % alpha, FALSE);
}
// let overlay image and text play well together
@@ -686,20 +777,21 @@ void LLButton::draw()
center_x++;
}
+ center_y += (mImageOverlayBottomPad - mImageOverlayTopPad);
// fade out overlay images on disabled buttons
LLColor4 overlay_color = mImageOverlayColor.get();
- if (!getEnabled())
+ if (!enabled)
{
overlay_color.mV[VALPHA] = 0.5f;
}
+ overlay_color.mV[VALPHA] *= alpha;
switch(mImageOverlayAlignment)
{
case LLFontGL::LEFT:
- text_left += overlay_width + 1;
- text_width -= overlay_width + 1;
+ text_left += overlay_width + mImgOverlayLabelSpace;
mImageOverlay->draw(
- mLeftHPad,
+ mLeftHPad,
center_y - (overlay_height / 2),
overlay_width,
overlay_height,
@@ -714,10 +806,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,
@@ -741,7 +832,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:
@@ -757,28 +848,35 @@ void LLButton::draw()
x++;
}
- mGLFont->render(label, 0, (F32)x, (F32)(LLBUTTON_V_PAD + y_offset),
- label_color,
+ // *NOTE: mantipov: before mUseEllipses is implemented in EXT-279 U32_MAX has been passed as
+ // max_chars.
+ // LLFontGL::render expects S32 max_chars variable but process in a separate way -1 value.
+ // Due to U32_MAX is equal to S32 -1 value I have rest this value for non-ellipses mode.
+ // Not sure if it is really needed. Probably S32_MAX should be always passed as max_chars.
+ mLastDrawCharsCount = mGLFont->render(label, 0,
+ (F32)x,
+ (F32)(mBottomVPad + y_offset),
+ label_color % alpha,
mHAlign, LLFontGL::BOTTOM,
LLFontGL::NORMAL,
mDropShadowedText ? LLFontGL::DROP_SHADOW_SOFT : LLFontGL::NO_SHADOW,
- U32_MAX, text_width,
- NULL, FALSE, FALSE);
+ S32_MAX, text_width,
+ NULL, mUseEllipses);
}
- LLView::draw();
+ LLUICtrl::draw();
}
-void LLButton::drawBorder(const LLColor4& color, S32 size)
+void LLButton::drawBorder(LLUIImage* imagep, const LLColor4& color, S32 size)
{
- if (mImagep.isNull()) return;
+ if (imagep == NULL) return;
if (mScaleImage)
{
- mImagep->drawBorder(getLocalRect(), color, size);
+ imagep->drawBorder(getLocalRect(), color, size);
}
else
{
- mImagep->drawBorder(0, 0, color, size);
+ imagep->drawBorder(0, 0, color, size);
}
}
@@ -840,16 +938,6 @@ void LLButton::setLabelSelected( const LLStringExplicit& label )
mSelectedLabel = label;
}
-void LLButton::setDisabledLabel( const LLStringExplicit& label )
-{
- mDisabledLabel = label;
-}
-
-void LLButton::setDisabledSelectedLabel( const LLStringExplicit& label )
-{
- mDisabledSelectedLabel = label;
-}
-
void LLButton::setImageUnselected(LLPointer<LLUIImage> image)
{
mImageUnselected = image;
@@ -864,25 +952,11 @@ void LLButton::autoResize()
LLUIString label;
if(getToggleState())
{
- if( getEnabled() || mDisabledSelectedLabel.empty() )
- {
- label = mSelectedLabel;
- }
- else
- {
- label = mDisabledSelectedLabel;
- }
+ label = mSelectedLabel;
}
else
{
- if( getEnabled() || mDisabledLabel.empty() )
- {
- label = mUnselectedLabel;
- }
- else
- {
- label = mDisabledLabel;
- }
+ label = mUnselectedLabel;
}
resize(label);
}
@@ -904,9 +978,8 @@ void LLButton::resize(LLUIString label)
}
void LLButton::setImages( const std::string &image_name, const std::string &selected_name )
{
- setImageUnselected(image_name);
- setImageSelected(selected_name);
-
+ setImageUnselected(LLUI::getUIImage(image_name));
+ setImageSelected(LLUI::getUIImage(selected_name));
}
void LLButton::setImageSelected(LLPointer<LLUIImage> image)
@@ -924,7 +997,6 @@ void LLButton::setColor(const LLColor4& color)
setImageColor(color);
}
-
void LLButton::setImageDisabled(LLPointer<LLUIImage> image)
{
mImageDisabled = image;
@@ -939,17 +1011,9 @@ void LLButton::setImageDisabledSelected(LLPointer<LLUIImage> image)
mFadeWhenDisabled = TRUE;
}
-void LLButton::setDisabledImages( const std::string &image_name, const std::string &selected_name)
+void LLButton::setImagePressed(LLPointer<LLUIImage> image)
{
- setDisabledImages( image_name, selected_name, mImageColor.get());
- mFadeWhenDisabled = TRUE;
-}
-
-void LLButton::setDisabledImages( const std::string &image_name, const std::string &selected_name, const LLColor4& c )
-{
- setImageDisabled(image_name);
- setImageDisabledSelected(selected_name);
- mDisabledImageColor = c;
+ mImagePressed = image;
}
void LLButton::setImageHoverSelected(LLPointer<LLUIImage> image)
@@ -962,10 +1026,9 @@ void LLButton::setImageHoverUnselected(LLPointer<LLUIImage> image)
mImageHoverUnselected = image;
}
-void LLButton::setHoverImages( const std::string& image_name, const std::string& selected_name )
+void LLButton::setImageFlash(LLPointer<LLUIImage> image)
{
- setImageHoverUnselected(image_name);
- setImageHoverSelected(selected_name);
+ mImageFlash = image;
}
void LLButton::setImageOverlay(const std::string& image_name, LLFontGL::HAlign alignment, const LLColor4& color)
@@ -982,6 +1045,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();
@@ -1005,42 +1082,6 @@ S32 round_up(S32 grid, S32 value)
}
}
-void LLButton::setImageUnselected(const std::string &image_name)
-{
- setImageUnselected(LLUI::getUIImage(image_name));
- mImageUnselectedName = image_name;
-}
-
-void LLButton::setImageSelected(const std::string &image_name)
-{
- setImageSelected(LLUI::getUIImage(image_name));
- mImageSelectedName = image_name;
-}
-
-void LLButton::setImageHoverSelected(const std::string &image_name)
-{
- setImageHoverSelected(LLUI::getUIImage(image_name));
- mImageHoverSelectedName = image_name;
-}
-
-void LLButton::setImageHoverUnselected(const std::string &image_name)
-{
- setImageHoverUnselected(LLUI::getUIImage(image_name));
- mImageHoverUnselectedName = image_name;
-}
-
-void LLButton::setImageDisabled(const std::string &image_name)
-{
- setImageDisabled(LLUI::getUIImage(image_name));
- mImageDisabledName = image_name;
-}
-
-void LLButton::setImageDisabledSelected(const std::string &image_name)
-{
- setImageDisabledSelected(LLUI::getUIImage(image_name));
- mImageDisabledSelectedName = image_name;
-}
-
void LLButton::addImageAttributeToXML(LLXMLNodePtr node,
const std::string& image_name,
const LLUUID& image_id,
@@ -1056,24 +1097,6 @@ void LLButton::addImageAttributeToXML(LLXMLNodePtr node,
}
}
-void clicked_help(void* data)
-{
- LLButton* self = (LLButton*)data;
- if (!self) return;
-
- if (!LLUI::sHtmlHelp)
- {
- return;
- }
-
- LLUI::sHtmlHelp->show(self->getHelpURL());
-}
-
-void LLButton::setHelpURLCallback(const std::string &help_url)
-{
- mHelpURL = help_url;
- setClickedCallback(clicked_help,this);
-}
// static
void LLButton::toggleFloaterAndSetToggleState(LLUICtrl* ctrl, const LLSD& sdname)
@@ -1094,28 +1117,52 @@ void LLButton::setFloaterToggle(LLUICtrl* ctrl, const LLSD& sdname)
// Get the visibility control name for the floater
std::string vis_control_name = LLFloaterReg::declareVisibilityControl(sdname.asString());
// Set the button control value (toggle state) to the floater visibility control (Sets the value as well)
- button->setControlVariable(LLUI::sSettingGroups["floater"]->getControl(vis_control_name));
+ button->setControlVariable(LLFloater::getControlGroup()->getControl(vis_control_name));
// Set the clicked callback to toggle the floater
button->setClickedCallback(boost::bind(&LLFloaterReg::toggleFloaterInstance, sdname));
}
-void LLButton::resetMouseDownTimer()
+// static
+void LLButton::setDockableFloaterToggle(LLUICtrl* ctrl, const LLSD& sdname)
{
- mMouseDownTimer.stop();
- mMouseDownTimer.reset();
+ LLButton* button = dynamic_cast<LLButton*>(ctrl);
+ if (!button)
+ return;
+ // Get the visibility control name for the floater
+ std::string vis_control_name = LLFloaterReg::declareVisibilityControl(sdname.asString());
+ // Set the button control value (toggle state) to the floater visibility control (Sets the value as well)
+ button->setControlVariable(LLFloater::getControlGroup()->getControl(vis_control_name));
+ // Set the clicked callback to toggle the floater
+ button->setClickedCallback(boost::bind(&LLDockableFloater::toggleInstance, sdname));
}
-
-// *TODO: Remove this function after the initial XUI XML re-export pass.
// static
-void LLButton::setupParamsForExport(Params& p, LLView* parent)
+void LLButton::showHelp(LLUICtrl* ctrl, const LLSD& sdname)
{
- std::string label = p.label;
- if (label.empty())
+ // search back through the button's parents for a panel
+ // with a help_topic string defined
+ std::string help_topic;
+ if (LLUI::sHelpImpl &&
+ ctrl->findHelpTopic(help_topic))
{
- //if our label is empty this is a picture style button
- p.picture_style = true;
+ LLUI::sHelpImpl->showTopic(help_topic);
+ return; // success
}
- LLUICtrl::setupParamsForExport(p, parent);
+ // display an error if we can't find a help_topic string.
+ // fix this by adding a help_topic attribute to the xui file
+ LLNotificationsUtil::add("UnableToFindHelpTopic");
+}
+
+void LLButton::resetMouseDownTimer()
+{
+ mMouseDownTimer.stop();
+ mMouseDownTimer.reset();
+}
+
+
+BOOL LLButton::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ // just treat a double click as a second click
+ return handleMouseDown(x, y, mask);
}
diff --git a/indra/llui/llbutton.h b/indra/llui/llbutton.h
index c7969e260d..d87ceb7c42 100644
--- a/indra/llui/llbutton.h
+++ b/indra/llui/llbutton.h
@@ -2,31 +2,25 @@
* @file llbutton.h
* @brief Header for buttons
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -49,7 +43,6 @@
// PLEASE please use these "constants" when building your own buttons.
// They are loaded from settings.xml at run time.
extern S32 LLBUTTON_H_PAD;
-extern S32 LLBUTTON_V_PAD;
extern S32 BTN_HEIGHT_SMALL;
extern S32 BTN_HEIGHT;
@@ -74,8 +67,9 @@ public:
{
// text label
Optional<std::string> label_selected;
- Optional<bool> label_dropshadow;
+ Optional<bool> label_shadow;
Optional<bool> auto_resize;
+ Optional<bool> use_ellipses;
// images
Optional<LLUIImage*> image_unselected,
@@ -84,6 +78,9 @@ public:
image_hover_unselected,
image_disabled_selected,
image_disabled,
+ image_flash,
+ image_pressed,
+ image_pressed_selected,
image_overlay;
Optional<std::string> image_overlay_alignment;
@@ -102,20 +99,28 @@ public:
// layout
Optional<S32> pad_right;
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,
- mouse_up_callback,
- mouse_held_callback;
+ mouse_down_callback,
+ mouse_up_callback,
+ mouse_held_callback;
// misc
Optional<bool> is_toggle,
scale_image,
- commit_on_return,
- picture_style; //if true, don't display label
+ commit_on_return;
- Optional<std::string> help_url;
Optional<F32> hover_glow_amount;
Optional<TimeIntervalParam> held_down_delay;
@@ -127,6 +132,8 @@ protected:
LLButton(const Params&);
public:
+
+ ~LLButton();
// For backward compatability only
typedef boost::function<void(void*)> button_callback_t;
@@ -139,6 +146,7 @@ public:
virtual BOOL handleHover(S32 x, S32 y, MASK mask);
virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask);
virtual BOOL handleRightMouseUp(S32 x, S32 y, MASK mask);
+ virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask);
virtual void draw();
/*virtual*/ BOOL postBuild();
@@ -150,21 +158,21 @@ public:
void setUnselectedLabelColor( const LLColor4& c ) { mUnselectedLabelColor = c; }
void setSelectedLabelColor( const LLColor4& c ) { mSelectedLabelColor = c; }
+ void setUseEllipses( BOOL use_ellipses ) { mUseEllipses = use_ellipses; }
- boost::signals::connection setClickedCallback( const commit_signal_t::slot_type& cb ); // mouse down and up within button
- boost::signals::connection setMouseDownCallback( const commit_signal_t::slot_type& cb );
- boost::signals::connection setMouseUpCallback( const commit_signal_t::slot_type& cb ); // mouse up, EVEN IF NOT IN BUTTON
- // Passes a 'count' parameter in the commit param payload, i.e. param["count"])
- boost::signals::connection setHeldDownCallback( const commit_signal_t::slot_type& cb ); // Mouse button held down and in button
- boost::signals::connection setRightClickedCallback( const commit_signal_t::slot_type& cb ); // right mouse down and up within button
+ boost::signals2::connection setClickedCallback( const commit_signal_t::slot_type& cb ); // mouse down and up within button
+ boost::signals2::connection setMouseDownCallback( const commit_signal_t::slot_type& cb );
+ boost::signals2::connection setMouseUpCallback( const commit_signal_t::slot_type& cb ); // mouse up, EVEN IF NOT IN BUTTON
+ // Passes a 'count' parameter in the commit param payload, i.e. param["count"])
+ boost::signals2::connection setHeldDownCallback( const commit_signal_t::slot_type& cb ); // Mouse button held down and in button
// *TODO: Deprecate (for backwards compatability only)
- boost::signals::connection setClickedCallback( button_callback_t cb, void* data );
- boost::signals::connection setMouseDownCallback( button_callback_t cb, void* data );
- boost::signals::connection setMouseUpCallback( button_callback_t cb, void* data );
- boost::signals::connection setHeldDownCallback( button_callback_t cb, void* data );
+ boost::signals2::connection setClickedCallback( button_callback_t cb, void* data );
+ boost::signals2::connection setMouseDownCallback( button_callback_t cb, void* data );
+ boost::signals2::connection setMouseUpCallback( button_callback_t cb, void* data );
+ boost::signals2::connection setHeldDownCallback( button_callback_t cb, void* data );
void setHeldDownDelay( F32 seconds, S32 frames = 0) { mHeldDownDelay = seconds; mHeldDownFrameDelay = frames; }
@@ -174,6 +182,7 @@ public:
BOOL getToggleState() const;
void setToggleState(BOOL b);
+ void setHighlight(bool b);
void setFlashing( BOOL b );
BOOL getFlashing() const { return mFlashing; }
@@ -182,38 +191,44 @@ 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); }
void setImageColor(const std::string& color_control);
void setImageColor(const LLColor4& c);
- virtual void setColor(const LLColor4& c);
+ /*virtual*/ void setColor(const LLColor4& c);
void setImages(const std::string &image_name, const std::string &selected_name);
- void setDisabledImages(const std::string &image_name, const std::string &selected_name);
- void setDisabledImages(const std::string &image_name, const std::string &selected_name, const LLColor4& c);
- void setHoverImages(const std::string &image_name, const std::string &selected_name);
-
void setDisabledImageColor(const LLColor4& c) { mDisabledImageColor = c; }
void setDisabledSelectedLabelColor( const LLColor4& c ) { mDisabledSelectedLabelColor = c; }
void setImageOverlay(const std::string& image_name, LLFontGL::HAlign alignment = LLFontGL::HCENTER, const LLColor4& color = LLColor4::white);
+ void setImageOverlay(const LLUUID& image_id, LLFontGL::HAlign alignment = LLFontGL::HCENTER, const LLColor4& color = LLColor4::white);
LLPointer<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);
virtual BOOL setLabelArg( const std::string& key, const LLStringExplicit& text );
void setLabelUnselected(const LLStringExplicit& label);
void setLabelSelected(const LLStringExplicit& label);
- void setDisabledLabel(const LLStringExplicit& disabled_label);
- void setDisabledSelectedLabel(const LLStringExplicit& disabled_label);
void setDisabledLabelColor( const LLColor4& c ) { mDisabledLabelColor = c; }
void setFont(const LLFontGL *font)
{ mGLFont = ( font ? font : LLFontGL::getFontSansSerif()); }
+ const LLFontGL* getFont() const { return mGLFont; }
+
+
+ S32 getLastDrawCharsCount() const { return mLastDrawCharsCount; }
+
void setScaleImage(BOOL scale) { mScaleImage = scale; }
BOOL getScaleImage() const { return mScaleImage; }
@@ -223,140 +238,127 @@ public:
void setHoverGlowStrength(F32 strength) { mHoverGlowStrength = strength; }
- void setImageUnselected(const std::string &image_name);
- const std::string& getImageUnselectedName() const { return mImageUnselectedName; }
- void setImageSelected(const std::string &image_name);
- const std::string& getImageSelectedName() const { return mImageSelectedName; }
- void setImageHoverSelected(const std::string &image_name);
- void setImageHoverUnselected(const std::string &image_name);
- void setImageDisabled(const std::string &image_name);
- void setImageDisabledSelected(const std::string &image_name);
-
void setImageUnselected(LLPointer<LLUIImage> image);
void setImageSelected(LLPointer<LLUIImage> image);
void setImageHoverSelected(LLPointer<LLUIImage> image);
void setImageHoverUnselected(LLPointer<LLUIImage> image);
void setImageDisabled(LLPointer<LLUIImage> image);
void setImageDisabledSelected(LLPointer<LLUIImage> image);
+ void setImageFlash(LLPointer<LLUIImage> image);
+ void setImagePressed(LLPointer<LLUIImage> image);
void setCommitOnReturn(BOOL commit) { mCommitOnReturn = commit; }
BOOL getCommitOnReturn() const { return mCommitOnReturn; }
- void setHelpURLCallback(const std::string &help_url);
- const std::string& getHelpURL() const { return mHelpURL; }
-
static void onHeldDown(void *userdata); // to be called by gIdleCallbacks
static void toggleFloaterAndSetToggleState(LLUICtrl* ctrl, const LLSD& sdname);
static void setFloaterToggle(LLUICtrl* ctrl, const LLSD& sdname);
+ static void setDockableFloaterToggle(LLUICtrl* ctrl, const LLSD& sdname);
+ static void showHelp(LLUICtrl* ctrl, const LLSD& sdname);
+
+ void setForcePressedState(bool b) { mForcePressedState = b; }
-protected:
+ void setAutoResize(bool auto_resize) { mAutoResize = auto_resize; }
- virtual void drawBorder(const LLColor4& color, S32 size);
-
- void setImageUnselectedID(const LLUUID &image_id);
- const LLUUID& getImageUnselectedID() const { return mImageUnselectedID; }
- void setImageSelectedID(const LLUUID &image_id);
- const LLUUID& getImageSelectedID() const { return mImageSelectedID; }
- void setImageHoverSelectedID(const LLUUID &image_id);
- void setImageHoverUnselectedID(const LLUUID &image_id);
- void setImageDisabledID(const LLUUID &image_id);
- void setImageDisabledSelectedID(const LLUUID &image_id);
- const LLPointer<LLUIImage>& getImageUnselected() const { return mImageUnselected; }
- const LLPointer<LLUIImage>& getImageSelected() const { return mImageSelected; }
- void resetMouseDownTimer();
+protected:
+ LLPointer<LLUIImage> getImageUnselected() const { return mImageUnselected; }
+ LLPointer<LLUIImage> getImageSelected() const { return mImageSelected; }
LLFrameTimer mMouseDownTimer;
- // If the label is empty, set the picture_style attribute
- static void setupParamsForExport(Params& p, LLView* parent);
+private:
+ void drawBorder(LLUIImage* imagep, const LLColor4& color, S32 size);
+ void resetMouseDownTimer();
private:
- commit_signal_t mMouseDownSignal;
- commit_signal_t mMouseUpSignal;
- commit_signal_t mHeldDownSignal;
+ commit_signal_t* mMouseDownSignal;
+ commit_signal_t* mMouseUpSignal;
+ commit_signal_t* mHeldDownSignal;
- const LLFontGL *mGLFont;
+ const LLFontGL* mGLFont;
- S32 mMouseDownFrame;
- S32 mMouseHeldDownCount; // Counter for parameter passed to held-down callback
- F32 mHeldDownDelay; // seconds, after which held-down callbacks get called
- S32 mHeldDownFrameDelay; // frames, after which held-down callbacks get called
+ S32 mMouseDownFrame;
+ S32 mMouseHeldDownCount; // Counter for parameter passed to held-down callback
+ F32 mHeldDownDelay; // seconds, after which held-down callbacks get called
+ S32 mHeldDownFrameDelay; // frames, after which held-down callbacks get called
+ S32 mLastDrawCharsCount;
LLPointer<LLUIImage> mImageOverlay;
LLFontGL::HAlign mImageOverlayAlignment;
- LLUIColor mImageOverlayColor;
+ LLUIColor mImageOverlayColor;
LLPointer<LLUIImage> mImageUnselected;
LLUIString mUnselectedLabel;
- LLUIColor mUnselectedLabelColor;
+ LLUIColor mUnselectedLabelColor;
LLPointer<LLUIImage> mImageSelected;
LLUIString mSelectedLabel;
- LLUIColor mSelectedLabelColor;
+ LLUIColor mSelectedLabelColor;
LLPointer<LLUIImage> mImageHoverSelected;
LLPointer<LLUIImage> mImageHoverUnselected;
LLPointer<LLUIImage> mImageDisabled;
- LLUIString mDisabledLabel;
- LLUIColor mDisabledLabelColor;
+ LLUIColor mDisabledLabelColor;
LLPointer<LLUIImage> mImageDisabledSelected;
LLUIString mDisabledSelectedLabel;
- LLUIColor mDisabledSelectedLabelColor;
+ LLUIColor mDisabledSelectedLabelColor;
+
+ LLPointer<LLUIImage> mImagePressed;
+ LLPointer<LLUIImage> mImagePressedSelected;
- LLUUID mImageUnselectedID;
- LLUUID mImageSelectedID;
- LLUUID mImageHoverSelectedID;
- LLUUID mImageHoverUnselectedID;
- LLUUID mImageDisabledID;
- LLUUID mImageDisabledSelectedID;
- std::string mImageUnselectedName;
- std::string mImageSelectedName;
- std::string mImageHoverSelectedName;
- std::string mImageHoverUnselectedName;
- std::string mImageDisabledName;
- std::string mImageDisabledSelectedName;
+ /* There are two ways an image can flash- by making changes in color according to flash_color attribute
+ or by changing icon from current to the one specified in image_flash. Second way is used only if
+ flash icon name is set in attributes(by default it isn't). First way is used otherwise. */
+ LLPointer<LLUIImage> mImageFlash;
- LLUIColor mHighlightColor;
- LLUIColor mFlashBgColor;
+ LLUIColor mHighlightColor;
+ LLUIColor mFlashBgColor;
- LLUIColor mImageColor;
- LLUIColor mDisabledImageColor;
+ LLUIColor mImageColor;
+ LLUIColor mDisabledImageColor;
- BOOL mIsToggle;
- BOOL mScaleImage;
+ BOOL mIsToggle;
+ BOOL mScaleImage;
- BOOL mDropShadowedText;
- BOOL mAutoResize;
- BOOL mBorderEnabled;
+ BOOL mDropShadowedText;
+ BOOL mAutoResize;
+ BOOL mUseEllipses;
+ BOOL mBorderEnabled;
- BOOL mFlashing;
+ BOOL mFlashing;
- LLFontGL::HAlign mHAlign;
- S32 mLeftHPad;
- S32 mRightHPad;
+ LLFontGL::HAlign mHAlign;
+ S32 mLeftHPad;
+ S32 mRightHPad;
+ S32 mBottomVPad; // under text label
- F32 mHoverGlowStrength;
- F32 mCurGlowStrength;
+ S32 mImageOverlayTopPad;
+ S32 mImageOverlayBottomPad;
- BOOL mNeedsHighlight;
- BOOL mCommitOnReturn;
- BOOL mFadeWhenDisabled;
+ /*
+ * Space between image_overlay and label
+ */
+ S32 mImgOverlayLabelSpace;
- std::string mHelpURL;
+ F32 mHoverGlowStrength;
+ F32 mCurGlowStrength;
- LLPointer<LLUIImage> mImagep;
+ BOOL mNeedsHighlight;
+ BOOL mCommitOnReturn;
+ BOOL mFadeWhenDisabled;
+ bool mForcePressedState;
+ bool mMouseOver;
- LLFrameTimer mFlashingTimer;
+ LLFrameTimer mFlashingTimer;
};
-#ifdef LL_WINDOWS
-#ifndef INSTANTIATE_GETCHILD_BUTTON
-#pragma warning (disable : 4231)
-extern template LLButton* LLView::getChild<LLButton>( const std::string& name, BOOL recurse, BOOL create_if_missing ) const;
-#endif
+// Build time optimization, generate once in .cpp file
+#ifndef LLBUTTON_CPP
+extern template class LLButton* LLView::getChild<class LLButton>(
+ const std::string& name, BOOL recurse) const;
#endif
#endif // LL_LLBUTTON_H
diff --git a/indra/llui/llcallbackmap.h b/indra/llui/llcallbackmap.h
index 97b1e2fc50..0a10877c09 100644
--- a/indra/llui/llcallbackmap.h
+++ b/indra/llui/llcallbackmap.h
@@ -2,31 +2,25 @@
* @file llcallbackmap.h
* @brief LLCallbackMap base class
*
- * $LicenseInfo:firstyear=2006&license=viewergpl$
- *
- * Copyright (c) 2006-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2006&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -35,12 +29,13 @@
#include <map>
#include <string>
+#include <boost/function.hpp>
class LLCallbackMap
{
public:
// callback definition.
- typedef void* (*callback_t)(void* data);
+ typedef boost::function<void* (void* data)> callback_t;
typedef std::map<std::string, LLCallbackMap> map_t;
typedef map_t::iterator map_iter_t;
diff --git a/indra/llui/llcheckboxctrl.cpp b/indra/llui/llcheckboxctrl.cpp
index 932a1b6297..cbc8f12472 100644
--- a/indra/llui/llcheckboxctrl.cpp
+++ b/indra/llui/llcheckboxctrl.cpp
@@ -2,39 +2,32 @@
* @file llcheckboxctrl.cpp
* @brief LLCheckBoxCtrl base class
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
// The mutants are coming!
-#define INSTANTIATE_GETCHILD_CHECKBOX
-
#include "linden_common.h"
+#define LLCHECKBOXCTRL_CPP
#include "llcheckboxctrl.h"
#include "llgl.h"
@@ -50,9 +43,11 @@
const U32 MAX_STRING_LENGTH = 10;
-template LLCheckBoxCtrl* LLView::getChild<LLCheckBoxCtrl>( const std::string& name, BOOL recurse, BOOL create_if_missing ) const;
+static LLDefaultChildRegistry::Register<LLCheckBoxCtrl> r("check_box");
-static LLDefaultWidgetRegistry::Register<LLCheckBoxCtrl> r("check_box");
+// Compiler optimization, generate extern template
+template class LLCheckBoxCtrl* LLView::getChild<class LLCheckBoxCtrl>(
+ const std::string& name, BOOL recurse) const;
LLCheckBoxCtrl::Params::Params()
: text_enabled_color("text_enabled_color"),
@@ -80,17 +75,6 @@ LLCheckBoxCtrl::LLCheckBoxCtrl(const LLCheckBoxCtrl::Params& p)
// must be big enough to hold all children
setUseBoundingRect(TRUE);
- // Label (add a little space to make sure text actually renders)
- const S32 FUDGE = 10;
- S32 text_width = mFont->getWidth( p.label ) + FUDGE;
- S32 text_height = llround(mFont->getLineHeight());
- LLRect label_rect;
- label_rect.setOriginAndSize(
- llcheckboxctrl_hpad + llcheckboxctrl_btn_size + llcheckboxctrl_spacing,
- llcheckboxctrl_vpad + 1, // padding to get better alignment
- text_width + llcheckboxctrl_hpad,
- text_height );
-
// *HACK Get rid of this with SL-55508...
// this allows blank check boxes and radio boxes for now
std::string local_label = p.label;
@@ -100,16 +84,26 @@ LLCheckBoxCtrl::LLCheckBoxCtrl(const LLCheckBoxCtrl::Params& p)
}
LLTextBox::Params tbparams = p.label_text;
- tbparams.rect(label_rect);
- tbparams.text(local_label);
+ tbparams.initial_value(local_label);
if (p.font.isProvided())
{
tbparams.font(p.font);
}
+ tbparams.text_color( p.enabled() ? p.text_enabled_color() : p.text_disabled_color() );
mLabel = LLUICtrlFactory::create<LLTextBox> (tbparams);
-
addChild(mLabel);
+ S32 text_width = mLabel->getTextBoundingRect().getWidth();
+ S32 text_height = llround(mFont->getLineHeight());
+ LLRect label_rect;
+ label_rect.setOriginAndSize(
+ llcheckboxctrl_hpad + llcheckboxctrl_btn_size + llcheckboxctrl_spacing,
+ llcheckboxctrl_vpad + 1, // padding to get better alignment
+ text_width + llcheckboxctrl_hpad,
+ text_height );
+ mLabel->setShape(label_rect);
+
+
// Button
// Note: button cover the label by extending all the way to the right.
LLRect btn_rect;
@@ -165,7 +159,6 @@ void LLCheckBoxCtrl::onCommit()
void LLCheckBoxCtrl::setEnabled(BOOL b)
{
LLView::setEnabled(b);
- mButton->setEnabled(b);
if (b)
{
@@ -190,8 +183,7 @@ void LLCheckBoxCtrl::reshape(S32 width, S32 height, BOOL called_from_parent)
static LLUICachedControl<S32> llcheckboxctrl_vpad ("UICheckboxctrlVPad", 0);
static LLUICachedControl<S32> llcheckboxctrl_btn_size ("UICheckboxctrlBtnSize", 0);
- const S32 FUDGE = 10;
- S32 text_width = mFont->getWidth( mLabel->getText() ) + FUDGE;
+ S32 text_width = mLabel->getTextBoundingRect().getWidth();
S32 text_height = llround(mFont->getLineHeight());
LLRect label_rect;
label_rect.setOriginAndSize(
@@ -199,7 +191,7 @@ void LLCheckBoxCtrl::reshape(S32 width, S32 height, BOOL called_from_parent)
llcheckboxctrl_vpad,
text_width,
text_height );
- mLabel->setRect(label_rect);
+ mLabel->setShape(label_rect);
LLRect btn_rect;
btn_rect.setOriginAndSize(
@@ -207,7 +199,7 @@ void LLCheckBoxCtrl::reshape(S32 width, S32 height, BOOL called_from_parent)
llcheckboxctrl_vpad,
llcheckboxctrl_btn_size + llcheckboxctrl_spacing + text_width,
llmax( text_height, llcheckboxctrl_btn_size() ) );
- mButton->setRect( btn_rect );
+ mButton->setShape( btn_rect );
LLUICtrl::reshape(width, height, called_from_parent);
}
diff --git a/indra/llui/llcheckboxctrl.h b/indra/llui/llcheckboxctrl.h
index fe719e3b6a..0147088280 100644
--- a/indra/llui/llcheckboxctrl.h
+++ b/indra/llui/llcheckboxctrl.h
@@ -2,31 +2,25 @@
* @file llcheckboxctrl.h
* @brief LLCheckBoxCtrl base class
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -65,7 +59,7 @@ public:
Optional<LLTextBox::Params> label_text;
Optional<LLButton::Params> check_button;
- Deprecated radio_style;
+ Ignored radio_style;
Params();
};
@@ -107,6 +101,7 @@ public:
std::string getLabel() const;
void setFont( const LLFontGL* font ) { mFont = font; }
+ const LLFontGL* getFont() { return mFont; }
virtual void setControlName(const std::string& control_name, LLView* context);
@@ -125,12 +120,10 @@ protected:
LLUIColor mTextDisabledColor;
};
-
-#ifdef LL_WINDOWS
-#ifndef INSTANTIATE_GETCHILD_CHECKBOX
-#pragma warning (disable : 4231)
-extern template LLCheckBoxCtrl* LLView::getChild<LLCheckBoxCtrl>( const std::string& name, BOOL recurse, BOOL create_if_missing ) const;
-#endif
+// Build time optimization, generate once in .cpp file
+#ifndef LLCHECKBOXCTRL_CPP
+extern template class LLCheckBoxCtrl* LLView::getChild<class LLCheckBoxCtrl>(
+ const std::string& name, BOOL recurse) const;
#endif
#endif // LL_LLCHECKBOXCTRL_H
diff --git a/indra/llui/llclipboard.cpp b/indra/llui/llclipboard.cpp
index 2cb8197a67..984c4ec5fb 100644
--- a/indra/llui/llclipboard.cpp
+++ b/indra/llui/llclipboard.cpp
@@ -2,31 +2,25 @@
* @file llclipboard.cpp
* @brief LLClipboard base class
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -61,6 +55,12 @@ void LLClipboard::copyFromSubstring(const LLWString &src, S32 pos, S32 len, cons
LLView::getWindow()->copyTextToClipboard( mString );
}
+void LLClipboard::copyFromString(const LLWString &src, const LLUUID& source_id )
+{
+ mSourceID = source_id;
+ mString = src;
+ LLView::getWindow()->copyTextToClipboard( mString );
+}
const LLWString& LLClipboard::getPasteWString( LLUUID* source_id )
{
diff --git a/indra/llui/llclipboard.h b/indra/llui/llclipboard.h
index 034a7a6aeb..24cb46c3f4 100644
--- a/indra/llui/llclipboard.h
+++ b/indra/llui/llclipboard.h
@@ -2,31 +2,25 @@
* @file llclipboard.h
* @brief LLClipboard base class
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -50,6 +44,7 @@ public:
(i.e. X11/Linux). */
void copyFromSubstring(const LLWString &copy_from, S32 pos, S32 len, const LLUUID& source_id = LLUUID::null );
+ void copyFromString(const LLWString &copy_from, const LLUUID& source_id = LLUUID::null );
BOOL canPasteString() const;
const LLWString& getPasteWString(LLUUID* source_id = NULL);
diff --git a/indra/llui/llcombobox.cpp b/indra/llui/llcombobox.cpp
index 2197d5432b..910bab9a97 100644
--- a/indra/llui/llcombobox.cpp
+++ b/indra/llui/llcombobox.cpp
@@ -2,39 +2,31 @@
* @file llcombobox.cpp
* @brief LLComboBox base class
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
// A control that displays the name of the chosen item, which when
// clicked shows a scrolling box of options.
-#define INSTANTIATE_GETCHILD_COMBOBOX
-
#include "linden_common.h"
// file includes
@@ -57,15 +49,14 @@
#include "lllineeditor.h"
#include "v2math.h"
#include "lluictrlfactory.h"
+#include "lltooltip.h"
// Globals
S32 LLCOMBOBOX_HEIGHT = 0;
S32 LLCOMBOBOX_WIDTH = 0;
S32 MAX_COMBO_WIDTH = 500;
-template LLComboBox* LLView::getChild<LLComboBox>( const std::string& name, BOOL recurse, BOOL create_if_missing ) const;
-
-static LLDefaultWidgetRegistry::Register<LLComboBox> register_combo_box("combo_box");
+static LLDefaultChildRegistry::Register<LLComboBox> register_combo_box("combo_box");
void LLComboBox::PreferredPositionValues::declareValues()
{
@@ -81,14 +72,15 @@ LLComboBox::ItemParams::ItemParams()
LLComboBox::Params::Params()
: allow_text_entry("allow_text_entry", false),
+ allow_new_values("allow_new_values", false),
show_text_as_tentative("show_text_as_tentative", true),
max_chars("max_chars", 20),
- arrow_image("arrow_image"),
list_position("list_position", BELOW),
items("item"),
combo_button("combo_button"),
combo_list("combo_list"),
- combo_editor("combo_editor")
+ combo_editor("combo_editor"),
+ drop_down_button("drop_down_button")
{
addSynonym(items, "combo_item");
}
@@ -100,23 +92,38 @@ LLComboBox::LLComboBox(const LLComboBox::Params& p)
mTextEntryTentative(p.show_text_as_tentative),
mHasAutocompletedText(false),
mAllowTextEntry(p.allow_text_entry),
+ mAllowNewValues(p.allow_new_values),
mMaxChars(p.max_chars),
mPrearrangeCallback(p.prearrange_callback()),
mTextEntryCallback(p.text_entry_callback()),
- mSelectionCallback(p.selection_callback()),
- mArrowImage(p.arrow_image),
- mListPosition(p.list_position)
+ mListPosition(p.list_position),
+ mLastSelectedIndex(-1),
+ mLabel(p.label)
{
// Text label button
- LLButton::Params button_params = p.combo_button;
- button_params.mouse_down_callback.function(boost::bind(&LLComboBox::onButtonDown, this));
+ LLButton::Params button_params = (mAllowTextEntry ? p.combo_button : p.drop_down_button);
+ button_params.mouse_down_callback.function(
+ boost::bind(&LLComboBox::onButtonMouseDown, this));
button_params.follows.flags(FOLLOWS_LEFT|FOLLOWS_BOTTOM|FOLLOWS_RIGHT);
button_params.rect(p.rect);
- button_params.pad_right(2);
+
+ if(mAllowTextEntry)
+ {
+ button_params.pad_right(2);
+ }
+
+ mArrowImage = button_params.image_unselected;
mButton = LLUICtrlFactory::create<LLButton>(button_params);
- mButton->setRightHPad(2); //redo to compensate for button hack that leaves space for a character
+
+
+ if(mAllowTextEntry)
+ {
+ //redo to compensate for button hack that leaves space for a character
+ //unless it is a "minimal combobox"(drop down)
+ mButton->setRightHPad(2);
+ }
addChild(mButton);
LLScrollListCtrl::Params params = p.combo_list;
@@ -128,6 +135,10 @@ LLComboBox::LLComboBox(const LLComboBox::Params& p)
mList = LLUICtrlFactory::create<LLScrollListCtrl>(params);
addChild(mList);
+ // Mouse-down on button will transfer mouse focus to the list
+ // Grab the mouse-up event and make sure the button state is correct
+ mList->setMouseUpCallback(boost::bind(&LLComboBox::onListMouseUp, this));
+
for (LLInitParam::ParamIterator<ItemParams>::const_iterator it = p.items().begin();
it != p.items().end();
++it)
@@ -135,7 +146,7 @@ LLComboBox::LLComboBox(const LLComboBox::Params& p)
LLScrollListItem::Params item_params = *it;
if (it->label.isProvided())
{
- item_params.cells.add().value(it->label());
+ item_params.columns.add().value(it->label());
}
mList->addRow(item_params);
@@ -143,7 +154,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)
@@ -170,15 +181,12 @@ BOOL LLComboBox::postBuild()
LLComboBox::~LLComboBox()
{
// children automatically deleted, including mMenu, mButton
-}
-
-void LLComboBox::setEnabled(BOOL enabled)
-{
- LLView::setEnabled(enabled);
- mButton->setEnabled(enabled);
+ // explicitly disconect this signal, since base class destructor might fire top lost
+ mTopLostSignalConnection.disconnect();
}
+
void LLComboBox::clear()
{
if (mTextEntry)
@@ -187,9 +195,8 @@ void LLComboBox::clear()
}
mButton->setLabelSelected(LLStringUtil::null);
mButton->setLabelUnselected(LLStringUtil::null);
- mButton->setDisabledLabel(LLStringUtil::null);
- mButton->setDisabledSelectedLabel(LLStringUtil::null);
mList->deselectAllItems();
+ mLastSelectedIndex = -1;
}
void LLComboBox::onCommit()
@@ -295,6 +302,7 @@ BOOL LLComboBox::setSimple(const LLStringExplicit& name)
if (found)
{
setLabel(name);
+ mLastSelectedIndex = mList->getFirstSelectedIndex();
}
return found;
@@ -309,14 +317,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();
@@ -358,6 +371,7 @@ void LLComboBox::setLabel(const LLStringExplicit& name)
if (mList->selectItemByLabel(name, FALSE))
{
mTextEntry->setTentative(FALSE);
+ mLastSelectedIndex = mList->getFirstSelectedIndex();
}
else
{
@@ -367,10 +381,7 @@ void LLComboBox::setLabel(const LLStringExplicit& name)
if (!mAllowTextEntry)
{
- mButton->setLabelUnselected(name);
- mButton->setLabelSelected(name);
- mButton->setDisabledLabel(name);
- mButton->setDisabledSelectedLabel(name);
+ mButton->setLabel(name);
}
}
@@ -386,6 +397,7 @@ BOOL LLComboBox::remove(const std::string& name)
{
mList->deleteSingleItem(mList->getItemIndex(item));
}
+ mLastSelectedIndex = mList->getFirstSelectedIndex();
}
return found;
@@ -396,7 +408,7 @@ BOOL LLComboBox::remove(S32 index)
if (index < mList->getItemCount())
{
mList->deleteSingleItem(index);
- setLabel(mList->getSelectedItemLabel());
+ setLabel(getSelectedItemLabel());
return TRUE;
}
return FALSE;
@@ -424,27 +436,21 @@ void LLComboBox::setButtonVisible(BOOL visible)
LLRect text_entry_rect(0, getRect().getHeight(), getRect().getWidth(), 0);
if (visible)
{
- text_entry_rect.mRight -= llmax(8,mArrowImage->getWidth()) + 2 * drop_shadow_button;
+ S32 arrow_width = mArrowImage ? mArrowImage->getWidth() : 0;
+ text_entry_rect.mRight -= llmax(8,arrow_width) + 2 * drop_shadow_button;
}
//mTextEntry->setRect(text_entry_rect);
mTextEntry->reshape(text_entry_rect.getWidth(), text_entry_rect.getHeight(), TRUE);
}
}
-void LLComboBox::draw()
-{
- mButton->setEnabled(getEnabled() /*&& !mList->isEmpty()*/);
-
- // Draw children normally
- LLUICtrl::draw();
-}
-
BOOL LLComboBox::setCurrentByIndex( S32 index )
{
BOOL found = mList->selectNthItem( index );
if (found)
{
- setLabel(mList->getSelectedItemLabel());
+ setLabel(getSelectedItemLabel());
+ mLastSelectedIndex = index;
}
return found;
}
@@ -466,14 +472,15 @@ void LLComboBox::createLineEditor(const LLComboBox::Params& p)
LLRect rect = getLocalRect();
if (mAllowTextEntry)
{
+ S32 arrow_width = mArrowImage ? mArrowImage->getWidth() : 0;
S32 shadow_size = drop_shadow_button;
- mButton->setRect(LLRect( getRect().getWidth() - llmax(8,mArrowImage->getWidth()) - 2 * shadow_size,
+ mButton->setRect(LLRect( getRect().getWidth() - llmax(8,arrow_width) - 2 * shadow_size,
rect.mTop, rect.mRight, rect.mBottom));
mButton->setTabStop(FALSE);
mButton->setHAlign(LLFontGL::HCENTER);
LLRect text_entry_rect(0, getRect().getHeight(), getRect().getWidth(), 0);
- text_entry_rect.mRight -= llmax(8,mArrowImage->getWidth()) + 2 * drop_shadow_button;
+ text_entry_rect.mRight -= llmax(8,arrow_width) + 2 * drop_shadow_button;
// clear label on button
std::string cur_label = mButton->getLabelSelected();
LLLineEditor::Params params = p.combo_editor;
@@ -482,10 +489,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.focus_lost_callback(NULL);
- 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);
@@ -501,7 +507,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);
@@ -605,50 +612,56 @@ 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()
{
- //*HACK: store the original value explicitly somewhere, not just in label
- std::string orig_selection = mAllowTextEntry ? mTextEntry->getText() : mButton->getLabelSelected();
-
- // assert selection in list
- mList->selectItemByLabel(orig_selection, FALSE);
+ if (mList->getVisible())
+ {
+ // assert selection in list
+ if(mAllowNewValues)
+ {
+ // mLastSelectedIndex = -1 means that we entered a new value, don't select
+ // any of existing items in this case.
+ if(mLastSelectedIndex >= 0)
+ mList->selectNthItem(mLastSelectedIndex);
+ }
+ else if(mLastSelectedIndex >= 0)
+ mList->selectNthItem(mLastSelectedIndex);
- mButton->setToggleState(FALSE);
- mList->setVisible(FALSE);
- mList->mouseOverHighlightNthItem(-1);
+ mButton->setToggleState(FALSE);
+ mList->setVisible(FALSE);
+ mList->mouseOverHighlightNthItem(-1);
- setUseBoundingRect(FALSE);
- if( gFocusMgr.getTopCtrl() == this )
- {
- gFocusMgr.setTopCtrl(NULL);
+ setUseBoundingRect(FALSE);
+ LLUI::removePopup(this);
+// updateBoundingRect();
}
}
-void LLComboBox::onButtonDown()
+void LLComboBox::onButtonMouseDown()
{
if (!mList->getVisible())
{
+ // this might change selection, so do it first
+ prearrangeList();
+
+ // highlight the last selected item from the original selection before potentially selecting a new item
+ // as visual cue to original value of combo box
LLScrollListItem* last_selected_item = mList->getLastSelectedItem();
if (last_selected_item)
{
- // highlight the original selection before potentially selecting a new item
mList->mouseOverHighlightNthItem(mList->getItemIndex(last_selected_item));
}
- prearrangeList();
-
if (mList->getItemCount() != 0)
{
showList();
@@ -660,6 +673,10 @@ void LLComboBox::onButtonDown()
if (mButton->hasMouseCapture())
{
gFocusMgr.setMouseCapture(mList);
+
+ // But keep the "pressed" look, which buttons normally lose when they
+ // lose focus
+ mButton->setForcePressedState(true);
}
}
else
@@ -669,6 +686,12 @@ void LLComboBox::onButtonDown()
}
+void LLComboBox::onListMouseUp()
+{
+ // In some cases this is the termination of a mouse click that started on
+ // the button, so clear its pressed state
+ mButton->setForcePressedState(false);
+}
//------------------------------------------------------------------
// static functions
@@ -676,12 +699,10 @@ void LLComboBox::onButtonDown()
void LLComboBox::onItemSelected(const LLSD& data)
{
- const std::string name = mList->getSelectedItemLabel();
-
- S32 cur_id = getCurrentIndex();
- if (cur_id != -1)
+ mLastSelectedIndex = getCurrentIndex();
+ if (mLastSelectedIndex != -1)
{
- setLabel(name);
+ setLabel(getSelectedItemLabel());
if (mAllowTextEntry)
{
@@ -689,53 +710,33 @@ void LLComboBox::onItemSelected(const LLSD& data)
mTextEntry->selectAll();
}
}
-
// hiding the list reasserts the old value stored in the text editor/dropdown button
hideList();
// commit does the reverse, asserting the value in the list
onCommit();
-
- // call the callback if it exists
- if(mSelectionCallback)
- {
- mSelectionCallback(this, data);
- }
}
-BOOL LLComboBox::handleToolTip(S32 x, S32 y, std::string& msg, LLRect* sticky_rect_screen)
+BOOL LLComboBox::handleToolTip(S32 x, S32 y, MASK mask)
{
std::string tool_tip;
- if(LLUICtrl::handleToolTip(x, y, msg, sticky_rect_screen))
+ if(LLUICtrl::handleToolTip(x, y, mask))
{
return TRUE;
}
- if (LLUI::sShowXUINames)
- {
- tool_tip = getShowNamesToolTip();
- }
- else
+ tool_tip = getToolTip();
+ if (tool_tip.empty())
{
- tool_tip = getToolTip();
- if (tool_tip.empty())
- {
- tool_tip = getSelectedItemLabel();
- }
+ tool_tip = getSelectedItemLabel();
}
if( !tool_tip.empty() )
{
- msg = tool_tip;
-
- // Convert rect local to screen coordinates
- localPointToScreen(
- 0, 0,
- &(sticky_rect_screen->mLeft), &(sticky_rect_screen->mBottom) );
- localPointToScreen(
- getRect().getWidth(), getRect().getHeight(),
- &(sticky_rect_screen->mRight), &(sticky_rect_screen->mTop) );
+ LLToolTipMgr::instance().show(LLToolTip::Params()
+ .message(tool_tip)
+ .sticky_rect(calcScreenRect()));
}
return TRUE;
}
@@ -826,11 +827,13 @@ void LLComboBox::onTextEntry(LLLineEditor* line_editor)
if (mList->selectItemByLabel(line_editor->getText(), FALSE))
{
line_editor->setTentative(FALSE);
+ mLastSelectedIndex = mList->getFirstSelectedIndex();
}
else
{
line_editor->setTentative(mTextEntryTentative);
mList->deselectAllItems();
+ mLastSelectedIndex = -1;
}
return;
}
@@ -897,16 +900,18 @@ void LLComboBox::updateSelection()
if (mList->selectItemByLabel(full_string, FALSE))
{
mTextEntry->setTentative(FALSE);
+ mLastSelectedIndex = mList->getFirstSelectedIndex();
}
else if (mList->selectItemByPrefix(left_wstring, FALSE))
{
- LLWString selected_item = utf8str_to_wstring(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());
mTextEntry->endSelection();
mTextEntry->setTentative(FALSE);
mHasAutocompletedText = TRUE;
+ mLastSelectedIndex = mList->getFirstSelectedIndex();
}
else // no matching items found
{
@@ -914,6 +919,7 @@ void LLComboBox::updateSelection()
mTextEntry->setText(wstring_to_utf8str(user_wstring)); // removes text added by autocompletion
mTextEntry->setTentative(mTextEntryTentative);
mHasAutocompletedText = FALSE;
+ mLastSelectedIndex = -1;
}
}
@@ -1000,7 +1006,8 @@ BOOL LLComboBox::setCurrentByID(const LLUUID& id)
if (found)
{
- setLabel(mList->getSelectedItemLabel());
+ setLabel(getSelectedItemLabel());
+ mLastSelectedIndex = mList->getFirstSelectedIndex();
}
return found;
@@ -1015,7 +1022,7 @@ BOOL LLComboBox::setSelectedByValue(const LLSD& value, BOOL selected)
BOOL found = mList->setSelectedByValue(value, selected);
if (found)
{
- setLabel(mList->getSelectedItemLabel());
+ setLabel(getSelectedItemLabel());
}
return found;
}
@@ -1054,3 +1061,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 bc98690a01..f369147ded 100644
--- a/indra/llui/llcombobox.h
+++ b/indra/llui/llcombobox.h
@@ -2,31 +2,25 @@
* @file llcombobox.h
* @brief LLComboBox base class
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -39,7 +33,6 @@
#include "llbutton.h"
#include "lluictrl.h"
#include "llctrlselectioninterface.h"
-#include "llimagegl.h"
#include "llrect.h"
#include "llscrolllistctrl.h"
#include "lllineeditor.h"
@@ -79,12 +72,11 @@ public:
: public LLInitParam::Block<Params, LLUICtrl::Params>
{
Optional<bool> allow_text_entry,
- show_text_as_tentative;
+ show_text_as_tentative,
+ allow_new_values;
Optional<S32> max_chars;
Optional<commit_callback_t> prearrange_callback,
- text_entry_callback,
- selection_callback;
- Optional<LLUIImage*> arrow_image;
+ text_entry_callback;
Optional<EPreferredPosition, PreferredPositionValues> list_position;
@@ -93,6 +85,8 @@ public:
Optional<LLScrollListCtrl::Params> combo_list;
Optional<LLLineEditor::Params> combo_editor;
+ Optional<LLButton::Params> drop_down_button;
+
Multiple<ItemParams> items;
Params();
@@ -110,12 +104,9 @@ protected:
public:
// LLView interface
- virtual void draw();
virtual void onFocusLost();
- virtual void setEnabled(BOOL enabled);
-
- virtual BOOL handleToolTip(S32 x, S32 y, std::string& msg, LLRect* sticky_rect);
+ virtual BOOL handleToolTip(S32 x, S32 y, MASK mask);
virtual BOOL handleKeyHere(KEY key, MASK mask);
virtual BOOL handleUnicodeCharHere(llwchar uni_char);
@@ -153,7 +144,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.
@@ -202,11 +193,11 @@ public:
void setPrearrangeCallback( commit_callback_t cb ) { mPrearrangeCallback = cb; }
void setTextEntryCallback( commit_callback_t cb ) { mTextEntryCallback = cb; }
- void setSelectionCallback( commit_callback_t cb ) { mSelectionCallback = cb; }
void setButtonVisible(BOOL visible);
- void onButtonDown();
+ void onButtonMouseDown();
+ void onListMouseUp();
void onItemSelected(const LLSD& data);
void onTextCommit(const LLSD& data);
@@ -227,18 +218,44 @@ protected:
private:
BOOL mAllowTextEntry;
+ BOOL mAllowNewValues;
S32 mMaxChars;
BOOL mTextEntryTentative;
commit_callback_t mPrearrangeCallback;
commit_callback_t mTextEntryCallback;
commit_callback_t mSelectionCallback;
+ boost::signals2::connection mTopLostSignalConnection;
+ S32 mLastSelectedIndex;
};
-#ifdef LL_WINDOWS
-#ifndef INSTANTIATE_GETCHILD_COMBOBOX
-#pragma warning (disable : 4231)
-extern template LLComboBox* LLView::getChild<LLComboBox>( const std::string& name, BOOL recurse, BOOL create_if_missing ) const;
-#endif
-#endif
+// 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 f1fc3d8f43..06bad1f371 100644
--- a/indra/llui/llconsole.cpp
+++ b/indra/llui/llconsole.cpp
@@ -2,31 +2,25 @@
* @file llconsole.cpp
* @brief a scrolling console output device
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -60,11 +54,15 @@ LLConsole* gConsole = NULL; // Created and destroyed in LLViewerWindow.
const F32 FADE_DURATION = 2.f;
const S32 MIN_CONSOLE_WIDTH = 200;
+static LLDefaultChildRegistry::Register<LLConsole> r("console");
+
LLConsole::LLConsole(const LLConsole::Params& p)
-: LLView(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())
{
@@ -94,7 +92,7 @@ void LLConsole::reshape(S32 width, S32 height, BOOL called_from_parent)
mConsoleWidth = new_width;
mConsoleHeight= new_height;
- LLView::reshape(new_width, new_height, called_from_parent);
+ LLUICtrl::reshape(new_width, new_height, called_from_parent);
for(paragraph_t::iterator paragraph_it = mParagraphs.begin(); paragraph_it != mParagraphs.end(); paragraph_it++)
{
@@ -120,6 +118,11 @@ void LLConsole::setFontSize(S32 size_index)
{
mFont = LLFontGL::getFontSansSerifHuge();
}
+ // Make sure the font exists
+ if (mFont == NULL)
+ {
+ mFont = LLFontGL::getFontDefault();
+ }
for(paragraph_t::iterator paragraph_it = mParagraphs.begin(); paragraph_it != mParagraphs.end(); paragraph_it++)
{
@@ -173,12 +176,12 @@ void LLConsole::draw()
// draw remaining lines
F32 y_pos = 0.f;
- LLUIImagePtr imagep = LLUI::getUIImage("rounded_square.tga");
+ LLUIImagePtr imagep = LLUI::getUIImage("Rounded_Square");
// F32 console_opacity = llclamp(gSavedSettings.getF32("ConsoleBackgroundOpacity"), 0.f, 1.f);
F32 console_opacity = llclamp(LLUI::sSettingGroups["config"]->getF32("ConsoleBackgroundOpacity"), 0.f, 1.f);
-// LLColor4 color = gSavedSkinSettings.getColor("ConsoleBackground");
- LLColor4 color = LLUI::sSettingGroups["color"]->getColor("ConsoleBackground");
+// LLColor4 color = LLUIColorTable::instance().getColor("ConsoleBackground");
+ LLColor4 color = LLUIColorTable::instance().getColor("ConsoleBackground");
color.mV[VALPHA] *= console_opacity;
F32 line_height = mFont->getLineHeight();
@@ -235,23 +238,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)
{
@@ -308,7 +294,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.
@@ -323,7 +310,7 @@ void LLConsole::Paragraph::updateLines(F32 screen_width, const LLFontGL* font, b
skip_chars = 0;
}
- U32 drawable = font->maxDrawableChars(mParagraphText.c_str()+paragraph_offset, screen_width, line_end - paragraph_offset, TRUE);
+ U32 drawable = font->maxDrawableChars(mParagraphText.c_str()+paragraph_offset, screen_width, line_end - paragraph_offset, LLFontGL::WORD_BOUNDARY_IF_POSSIBLE);
if (drawable != 0)
{
@@ -374,20 +361,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 );
-
-#if LL_WINDOWS && LL_LCD_COMPILE
- // add to LCD screen
- AddNewDebugConsoleToLCD(wline);
-#endif
+ 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();
+ }
+}
+
diff --git a/indra/llui/llconsole.h b/indra/llui/llconsole.h
index 65149b217f..bb8ea50bed 100644
--- a/indra/llui/llconsole.h
+++ b/indra/llui/llconsole.h
@@ -2,31 +2,25 @@
* @file llconsole.h
* @brief a simple console-style output device
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -34,14 +28,13 @@
#define LL_LLCONSOLE_H
#include "llfixedbuffer.h"
-#include "llview.h"
+#include "lluictrl.h"
#include "v4color.h"
#include <deque>
-class LLFontGL;
class LLSD;
-class LLConsole : public LLFixedBuffer, public LLView
+class LLConsole : public LLFixedBuffer, public LLUICtrl, public LLInstanceTracker<LLConsole>
{
public:
typedef enum e_font_size
@@ -51,14 +44,15 @@ public:
BIG = 1
} EFontSize;
- struct Params : public LLInitParam::Block<Params, LLView::Params>
+ struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
{
Optional<U32> max_lines;
Optional<F32> persist_time;
Optional<S32> font_size_index;
Params()
: max_lines("max_lines", LLUI::sSettingGroups["config"]->getS32("ConsoleMaxLines")),
- persist_time("persist_time", 0.f) // forever
+ persist_time("persist_time", 0.f), // forever
+ font_size_index("font_size_index")
{
mouse_opaque(false);
}
@@ -68,6 +62,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 +136,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/llcontainerview.cpp b/indra/llui/llcontainerview.cpp
index 40cc430e25..e01e331acf 100644
--- a/indra/llui/llcontainerview.cpp
+++ b/indra/llui/llcontainerview.cpp
@@ -2,31 +2,25 @@
* @file llcontainerview.cpp
* @brief Container for all statistics info
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -42,7 +36,12 @@
#include "llscrollcontainer.h"
#include "lluictrlfactory.h"
-static LLDefaultWidgetRegistry::Register<LLContainerView> r("container_view");
+static LLDefaultChildRegistry::Register<LLContainerView> r1("container_view");
+
+#include "llpanel.h"
+#include "llstatview.h"
+static ContainerViewRegistry::Register<LLStatView> r2("stat_view");
+static ContainerViewRegistry::Register<LLPanel> r3("panel", &LLPanel::fromXML);
LLContainerView::LLContainerView(const LLContainerView::Params& p)
: LLView(p),
@@ -127,35 +126,31 @@ void LLContainerView::draw()
void LLContainerView::reshape(S32 width, S32 height, BOOL called_from_parent)
{
- S32 desired_width = width;
- S32 desired_height = height;
+ LLRect scroller_rect;
+ scroller_rect.setOriginAndSize(0, 0, width, height);
if (mScrollContainer)
{
- BOOL dum_bool;
- mScrollContainer->calcVisibleSize(&desired_width, &desired_height, &dum_bool, &dum_bool);
+ scroller_rect = mScrollContainer->getContentWindowRect();
}
else
{
// if we're uncontained - make height as small as possible
- desired_height = 0;
+ scroller_rect.mTop = 0;
}
- arrange(desired_width, desired_height, called_from_parent);
+ arrange(scroller_rect.getWidth(), scroller_rect.getHeight(), called_from_parent);
// sometimes, after layout, our container will change size (scrollbars popping in and out)
// if so, attempt another layout
if (mScrollContainer)
{
- S32 new_container_width;
- S32 new_container_height;
- BOOL dum_bool;
- mScrollContainer->calcVisibleSize(&new_container_width, &new_container_height, &dum_bool, &dum_bool);
+ LLRect new_container_rect = mScrollContainer->getContentWindowRect();
- if ((new_container_width != desired_width) ||
- (new_container_height != desired_height)) // the container size has changed, attempt to arrange again
+ if ((new_container_rect.getWidth() != scroller_rect.getWidth()) ||
+ (new_container_rect.getHeight() != scroller_rect.getHeight())) // the container size has changed, attempt to arrange again
{
- arrange(new_container_width, new_container_height, called_from_parent);
+ arrange(new_container_rect.getWidth(), new_container_rect.getHeight(), called_from_parent);
}
}
}
diff --git a/indra/llui/llcontainerview.h b/indra/llui/llcontainerview.h
index 9f3d1ac7ad..7d3d5cf787 100644
--- a/indra/llui/llcontainerview.h
+++ b/indra/llui/llcontainerview.h
@@ -2,31 +2,25 @@
* @file llcontainerview.h
* @brief Container for all statistics info.
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -36,9 +30,13 @@
#include "stdtypes.h"
#include "lltextbox.h"
#include "llstatbar.h"
+#include "llview.h"
class LLScrollContainer;
+struct ContainerViewRegistry : public LLChildRegistry<ContainerViewRegistry>
+{};
+
class LLContainerView : public LLView
{
public:
@@ -55,6 +53,10 @@ public:
mouse_opaque(false);
}
};
+
+ // my valid children are stored in this registry
+ typedef ContainerViewRegistry child_registry_t;
+
protected:
LLContainerView(const Params& p);
friend class LLUICtrlFactory;
diff --git a/indra/llui/llctrlselectioninterface.cpp b/indra/llui/llctrlselectioninterface.cpp
index dac0939b67..7e886aff48 100644
--- a/indra/llui/llctrlselectioninterface.cpp
+++ b/indra/llui/llctrlselectioninterface.cpp
@@ -2,31 +2,25 @@
* @file llctrlselectioninterface.cpp
* @brief Programmatic selection of items in a list.
*
- * $LicenseInfo:firstyear=2006&license=viewergpl$
- *
- * Copyright (c) 2006-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2006&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "linden_common.h"
diff --git a/indra/llui/llctrlselectioninterface.h b/indra/llui/llctrlselectioninterface.h
index 45727d65ce..2cdcbd88fe 100644
--- a/indra/llui/llctrlselectioninterface.h
+++ b/indra/llui/llctrlselectioninterface.h
@@ -2,31 +2,25 @@
* @file llctrlselectioninterface.h
* @brief Programmatic selection of items in a list.
*
- * $LicenseInfo:firstyear=2006&license=viewergpl$
- *
- * Copyright (c) 2006-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2006&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llui/lldockablefloater.cpp b/indra/llui/lldockablefloater.cpp
new file mode 100644
index 0000000000..ca2dc644a4
--- /dev/null
+++ b/indra/llui/lldockablefloater.cpp
@@ -0,0 +1,245 @@
+/**
+ * @file lldockablefloater.cpp
+ * @brief Creates a panel of a specific kind for a toast
+ *
+ * $LicenseInfo:firstyear=2000&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "lldockablefloater.h"
+#include "llfloaterreg.h"
+
+//static
+LLHandle<LLFloater> LLDockableFloater::sInstanceHandle;
+
+//static
+void LLDockableFloater::init(LLDockableFloater* thiz)
+{
+ thiz->setDocked(thiz->mDockControl.get() != NULL
+ && thiz->mDockControl.get()->isDockVisible());
+ thiz->resetInstance();
+
+ // all dockable floaters should have close, dock and minimize buttons
+ thiz->setCanClose(TRUE);
+ thiz->setCanDock(true);
+ thiz->setCanMinimize(TRUE);
+ thiz->setOverlapsScreenChannel(false);
+ thiz->mForceDocking = false;
+}
+
+LLDockableFloater::LLDockableFloater(LLDockControl* dockControl,
+ const LLSD& key, const Params& params) :
+ LLFloater(key, params), mDockControl(dockControl), mUniqueDocking(true)
+{
+ init(this);
+ mUseTongue = true;
+}
+
+LLDockableFloater::LLDockableFloater(LLDockControl* dockControl, bool uniqueDocking,
+ const LLSD& key, const Params& params) :
+ LLFloater(key, params), mDockControl(dockControl), mUniqueDocking(uniqueDocking)
+{
+ init(this);
+ mUseTongue = true;
+}
+
+LLDockableFloater::LLDockableFloater(LLDockControl* dockControl, bool uniqueDocking,
+ bool useTongue, const LLSD& key, const Params& params) :
+ LLFloater(key, params), mDockControl(dockControl), mUseTongue(useTongue), mUniqueDocking(uniqueDocking)
+{
+ init(this);
+}
+
+LLDockableFloater::~LLDockableFloater()
+{
+}
+
+BOOL LLDockableFloater::postBuild()
+{
+ // Remember we should force docking when the floater is opened for the first time
+ if (mIsDockedStateForcedCallback != NULL && mIsDockedStateForcedCallback())
+ {
+ mForceDocking = true;
+ }
+
+ mDockTongue = LLUI::getUIImage("windows/Flyout_Pointer.png");
+ LLFloater::setDocked(true);
+ return LLView::postBuild();
+}
+
+//static
+void LLDockableFloater::toggleInstance(const LLSD& sdname)
+{
+ LLSD key;
+ std::string name = sdname.asString();
+
+ LLDockableFloater* instance =
+ dynamic_cast<LLDockableFloater*> (LLFloaterReg::findInstance(name));
+ // if floater closed or docked
+ if (instance == NULL || (instance && instance->isDocked()))
+ {
+ LLFloaterReg::toggleInstance(name, key);
+ // restore button toggle state
+ if (instance != NULL)
+ {
+ instance->storeVisibilityControl();
+ }
+ }
+ // if floater undocked
+ else if (instance != NULL)
+ {
+ instance->setMinimized(FALSE);
+ if (instance->getVisible())
+ {
+ instance->setVisible(FALSE);
+ }
+ else
+ {
+ instance->setVisible(TRUE);
+ gFloaterView->bringToFront(instance);
+ }
+ }
+}
+
+void LLDockableFloater::resetInstance()
+{
+ if (mUniqueDocking && sInstanceHandle.get() != this)
+ {
+ if (sInstanceHandle.get() != NULL && sInstanceHandle.get()->isDocked())
+ {
+ sInstanceHandle.get()->setVisible(FALSE);
+ }
+ sInstanceHandle = getHandle();
+ }
+}
+
+void LLDockableFloater::setVisible(BOOL visible)
+{
+ // Force docking if requested
+ if (visible && mForceDocking)
+ {
+ setCanDock(true);
+ setDocked(true);
+ mForceDocking = false;
+ }
+
+ if(visible && isDocked())
+ {
+ resetInstance();
+ }
+
+ if (visible && mDockControl.get() != NULL)
+ {
+ mDockControl.get()->repositionDockable();
+ }
+
+ if (visible)
+ {
+ LLFloater::setFrontmost(getAutoFocus());
+ }
+ LLFloater::setVisible(visible);
+}
+
+void LLDockableFloater::setMinimized(BOOL minimize)
+{
+ if(minimize)
+ {
+ setVisible(FALSE);
+ }
+}
+
+LLView * LLDockableFloater::getDockWidget()
+{
+ LLView * res = NULL;
+ if (getDockControl() != NULL) {
+ res = getDockControl()->getDock();
+ }
+
+ return res;
+}
+
+void LLDockableFloater::onDockHidden()
+{
+ setCanDock(FALSE);
+}
+
+void LLDockableFloater::onDockShown()
+{
+ if (!isMinimized())
+ {
+ setCanDock(TRUE);
+ }
+}
+
+void LLDockableFloater::setDocked(bool docked, bool pop_on_undock)
+{
+ if (mDockControl.get() != NULL && mDockControl.get()->isDockVisible())
+ {
+ if (docked)
+ {
+ resetInstance();
+ mDockControl.get()->on();
+ }
+ else
+ {
+ mDockControl.get()->off();
+ }
+
+ if (!docked && pop_on_undock)
+ {
+ // visually pop up a little bit to emphasize the undocking
+ translate(0, UNDOCK_LEAP_HEIGHT);
+ }
+ }
+
+ LLFloater::setDocked(docked, pop_on_undock);
+}
+
+void LLDockableFloater::draw()
+{
+ if (mDockControl.get() != NULL)
+ {
+ mDockControl.get()->repositionDockable();
+ if (isDocked())
+ {
+ mDockControl.get()->drawToungue();
+ }
+ }
+ LLFloater::draw();
+}
+
+void LLDockableFloater::setDockControl(LLDockControl* dockControl)
+{
+ mDockControl.reset(dockControl);
+ setDocked(isDocked());
+}
+
+const LLUIImagePtr& LLDockableFloater::getDockTongue()
+{
+ return mDockTongue;
+}
+
+LLDockControl* LLDockableFloater::getDockControl()
+{
+ return mDockControl.get();
+}
diff --git a/indra/llui/lldockablefloater.h b/indra/llui/lldockablefloater.h
new file mode 100644
index 0000000000..8deb6c1159
--- /dev/null
+++ b/indra/llui/lldockablefloater.h
@@ -0,0 +1,149 @@
+/**
+ * @file lldockablefloater.h
+ * @brief Creates a panel of a specific kind for a toast.
+ *
+ * $LicenseInfo:firstyear=2003&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_DOCKABLEFLOATER_H
+#define LL_DOCKABLEFLOATER_H
+
+#include "llerror.h"
+#include "llfloater.h"
+#include "lldockcontrol.h"
+
+/**
+ * Represents floater that can dock.
+ * In case impossibility deriving from LLDockableFloater use LLDockControl.
+ */
+class LLDockableFloater : public LLFloater
+{
+ static const U32 UNDOCK_LEAP_HEIGHT = 12;
+
+ static void init(LLDockableFloater* thiz);
+public:
+ LOG_CLASS(LLDockableFloater);
+ LLDockableFloater(LLDockControl* dockControl, const LLSD& key,
+ const Params& params = getDefaultParams());
+
+ /**
+ * Constructor.
+ * @param dockControl a pointer to the doc control instance
+ * @param uniqueDocking - a flag defines is docking should work as tab(at one
+ * moment only one docked floater can be shown), also this flag defines is dock
+ * tongue should be used.
+ * @params key a floater key.
+ * @params params a floater parameters
+ */
+ LLDockableFloater(LLDockControl* dockControl, bool uniqueDocking,
+ const LLSD& key, const Params& params = getDefaultParams());
+
+ /**
+ * Constructor.
+ * @param dockControl a pointer to the doc control instance
+ * @param uniqueDocking - a flag defines is docking should work as tab(at one
+ * moment only one docked floater can be shown).
+ * @praram useTongue - a flag defines is dock tongue should be used.
+ * @params key a floater key.
+ * @params params a floater parameters
+ */
+ LLDockableFloater(LLDockControl* dockControl, bool uniqueDocking,
+ bool useTongue, const LLSD& key,
+ const Params& params = getDefaultParams());
+
+ virtual ~LLDockableFloater();
+
+ static LLHandle<LLFloater> getInstanceHandle() { return sInstanceHandle; }
+
+ static void toggleInstance(const LLSD& sdname);
+
+ /**
+ * If descendant class overrides postBuild() in order to perform specific
+ * construction then it must still invoke its superclass' implementation.
+ */
+ /* virtula */BOOL postBuild();
+ /* virtual */void setDocked(bool docked, bool pop_on_undock = true);
+ /* virtual */void draw();
+
+ /**
+ * If descendant class overrides setVisible() then it must still invoke its
+ * superclass' implementation.
+ */
+ /*virtual*/ void setVisible(BOOL visible);
+
+ /**
+ * If descendant class overrides setMinimized() then it must still invoke its
+ * superclass' implementation.
+ */
+ /*virtual*/ void setMinimized(BOOL minimize);
+
+ LLView * getDockWidget();
+
+ virtual void onDockHidden();
+ virtual void onDockShown();
+
+ LLDockControl* getDockControl();
+
+ /**
+ * Returns true if screen channel should consider floater's size when drawing toasts.
+ *
+ * By default returns false.
+ */
+ virtual bool overlapsScreenChannel() { return mOverlapsScreenChannel && getVisible() && isDocked(); }
+ virtual void setOverlapsScreenChannel(bool overlaps) { mOverlapsScreenChannel = overlaps; }
+
+ bool getUniqueDocking() { return mUniqueDocking; }
+ bool getUseTongue() { return mUseTongue; }
+private:
+ /**
+ * Provides unique of dockable floater.
+ * If dockable floater already exists it should be closed.
+ */
+ void resetInstance();
+
+protected:
+ void setDockControl(LLDockControl* dockControl);
+ const LLUIImagePtr& getDockTongue();
+
+ // Checks if docking should be forced.
+ // It may be useful e.g. if floater created in mouselook mode (see EXT-5609)
+ boost::function<BOOL ()> mIsDockedStateForcedCallback;
+
+private:
+ std::auto_ptr<LLDockControl> mDockControl;
+ LLUIImagePtr mDockTongue;
+ static LLHandle<LLFloater> sInstanceHandle;
+ /**
+ * Provides possibility to define that dockable floaters can be docked
+ * non exclusively.
+ */
+ bool mUniqueDocking;
+
+ bool mUseTongue;
+
+ bool mOverlapsScreenChannel;
+
+ // Force docking when the floater is being shown for the first time.
+ bool mForceDocking;
+};
+
+#endif /* LL_DOCKABLEFLOATER_H */
diff --git a/indra/llui/lldockcontrol.cpp b/indra/llui/lldockcontrol.cpp
new file mode 100644
index 0000000000..d48674f306
--- /dev/null
+++ b/indra/llui/lldockcontrol.cpp
@@ -0,0 +1,342 @@
+/**
+ * @file lldockcontrol.cpp
+ * @brief Creates a panel of a specific kind for a toast
+ *
+ * $LicenseInfo:firstyear=2000&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "lldockcontrol.h"
+#include "lldockablefloater.h"
+
+LLDockControl::LLDockControl(LLView* dockWidget, LLFloater* dockableFloater,
+ const LLUIImagePtr& dockTongue, DocAt dockAt, get_allowed_rect_callback_t get_allowed_rect_callback) :
+ mDockWidget(dockWidget),
+ mDockableFloater(dockableFloater),
+ mDockTongue(dockTongue),
+ mDockTongueX(0),
+ mDockTongueY(0)
+{
+ mDockAt = dockAt;
+
+ if (dockableFloater->isDocked())
+ {
+ on();
+ }
+ else
+ {
+ off();
+ }
+
+ if (!(get_allowed_rect_callback))
+ {
+ mGetAllowedRectCallback = boost::bind(&LLDockControl::getAllowedRect, this, _1);
+ }
+ else
+ {
+ mGetAllowedRectCallback = get_allowed_rect_callback;
+ }
+
+ if (dockWidget != NULL)
+ {
+ repositionDockable();
+ }
+
+ if (mDockWidget != NULL)
+ {
+ mDockWidgetVisible = isDockVisible();
+ }
+ else
+ {
+ mDockWidgetVisible = false;
+ }
+}
+
+LLDockControl::~LLDockControl()
+{
+}
+
+void LLDockControl::setDock(LLView* dockWidget)
+{
+ mDockWidget = dockWidget;
+ if (mDockWidget != NULL)
+ {
+ repositionDockable();
+ mDockWidgetVisible = isDockVisible();
+ }
+ else
+ {
+ mDockWidgetVisible = false;
+ }
+}
+
+void LLDockControl::getAllowedRect(LLRect& rect)
+{
+ rect = mDockableFloater->getRootView()->getRect();
+}
+
+void LLDockControl::repositionDockable()
+{
+ LLRect dockRect = mDockWidget->calcScreenRect();
+ LLRect rootRect;
+ mGetAllowedRectCallback(rootRect);
+
+ // recalculate dockable position if dock position changed, dock visibility changed,
+ // root view rect changed or recalculation is forced
+ if (mPrevDockRect != dockRect || mDockWidgetVisible != isDockVisible()
+ || mRootRect != rootRect || mRecalculateDocablePosition)
+ {
+ // undock dockable and off() if dock not visible
+ if (!isDockVisible())
+ {
+ mDockableFloater->setDocked(false);
+ // force off() since dockable may not have dockControll at this time
+ off();
+ LLDockableFloater* dockable_floater =
+ dynamic_cast<LLDockableFloater*> (mDockableFloater);
+ if(dockable_floater != NULL)
+ {
+ dockable_floater->onDockHidden();
+ }
+ }
+ else
+ {
+ if(mEnabled)
+ {
+ moveDockable();
+ }
+ LLDockableFloater* dockable_floater =
+ dynamic_cast<LLDockableFloater*> (mDockableFloater);
+ if(dockable_floater != NULL)
+ {
+ dockable_floater->onDockShown();
+ }
+ }
+
+ mPrevDockRect = dockRect;
+ mRootRect = rootRect;
+ mRecalculateDocablePosition = false;
+ mDockWidgetVisible = isDockVisible();
+ }
+}
+
+bool LLDockControl::isDockVisible()
+{
+ bool res = true;
+
+ if (mDockWidget != NULL)
+ {
+ //we should check all hierarchy
+ res = mDockWidget->isInVisibleChain();
+ if (res)
+ {
+ LLRect dockRect = mDockWidget->calcScreenRect();
+
+ switch (mDockAt)
+ {
+ case LEFT: // to keep compiler happy
+ break;
+ case BOTTOM:
+ case TOP:
+ {
+ // check is dock inside parent rect
+ LLRect dockParentRect =
+ mDockWidget->getParent()->calcScreenRect();
+ if (dockRect.mRight <= dockParentRect.mLeft
+ || dockRect.mLeft >= dockParentRect.mRight)
+ {
+ res = false;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ }
+
+ return res;
+}
+
+void LLDockControl::moveDockable()
+{
+ // calculate new dockable position
+ LLRect dockRect = mDockWidget->calcScreenRect();
+ LLRect rootRect;
+ mGetAllowedRectCallback(rootRect);
+
+ bool use_tongue = false;
+ LLDockableFloater* dockable_floater =
+ dynamic_cast<LLDockableFloater*> (mDockableFloater);
+ if (dockable_floater != NULL)
+ {
+ use_tongue = dockable_floater->getUseTongue();
+ }
+
+ LLRect dockableRect = mDockableFloater->calcScreenRect();
+ S32 x = 0;
+ S32 y = 0;
+ LLRect dockParentRect;
+ switch (mDockAt)
+ {
+ case LEFT:
+ x = dockRect.mLeft;
+ y = dockRect.mTop + mDockTongue->getHeight() + dockableRect.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();
+ }
+
+ mDockTongueX = x + dockableRect.getWidth()/2 - mDockTongue->getWidth() / 2;
+
+ mDockTongueY = dockRect.mTop;
+ break;
+
+ case TOP:
+ x = dockRect.getCenterX() - dockableRect.getWidth() / 2;
+ y = dockRect.mTop + dockableRect.getHeight();
+ // 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.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
+ dockableRect.setLeftTopAndSize(x, y, dockableRect.getWidth(),
+ dockableRect.getHeight());
+ LLRect localDocableParentRect;
+ mDockableFloater->getParent()->screenRectToLocal(dockableRect,
+ &localDocableParentRect);
+ mDockableFloater->setRect(localDocableParentRect);
+
+ mDockableFloater->screenPointToLocal(mDockTongueX, mDockTongueY,
+ &mDockTongueX, &mDockTongueY);
+
+}
+
+void LLDockControl::on()
+{
+ if (isDockVisible())
+ {
+ mEnabled = true;
+ mRecalculateDocablePosition = true;
+ }
+}
+
+void LLDockControl::off()
+{
+ mEnabled = false;
+}
+
+void LLDockControl::forceRecalculatePosition()
+{
+ mRecalculateDocablePosition = true;
+}
+
+void LLDockControl::drawToungue()
+{
+ bool use_tongue = false;
+ LLDockableFloater* dockable_floater =
+ dynamic_cast<LLDockableFloater*> (mDockableFloater);
+ if (dockable_floater != NULL)
+ {
+ use_tongue = dockable_floater->getUseTongue();
+ }
+
+ if (mEnabled && use_tongue)
+ {
+ mDockTongue->draw(mDockTongueX, mDockTongueY);
+ }
+}
+
diff --git a/indra/llui/lldockcontrol.h b/indra/llui/lldockcontrol.h
new file mode 100644
index 0000000000..2e7359245f
--- /dev/null
+++ b/indra/llui/lldockcontrol.h
@@ -0,0 +1,94 @@
+/**
+ * @file lldockcontrol.h
+ * @brief Creates a panel of a specific kind for a toast.
+ *
+ * $LicenseInfo:firstyear=2003&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_DOCKCONTROL_H
+#define LL_DOCKCONTROL_H
+
+#include "llerror.h"
+#include "llview.h"
+#include "llfloater.h"
+#include "lluiimage.h"
+
+/**
+ * Provides services for docking of specified floater.
+ * This class should be used in case impossibility deriving from LLDockableFloater.
+ */
+class LLDockControl
+{
+public:
+ enum DocAt
+ {
+ TOP,
+ LEFT,
+ BOTTOM
+ };
+
+public:
+ // callback for a function getting a rect valid for control's position
+ typedef boost::function<void (LLRect& )> get_allowed_rect_callback_t;
+
+ LOG_CLASS(LLDockControl);
+ LLDockControl(LLView* dockWidget, LLFloater* dockableFloater,
+ const LLUIImagePtr& dockTongue, DocAt dockAt, get_allowed_rect_callback_t get_rect_callback = NULL);
+ virtual ~LLDockControl();
+
+public:
+ void on();
+ void off();
+ void forceRecalculatePosition();
+ void setDock(LLView* dockWidget);
+ LLView* getDock()
+ {
+ return mDockWidget;
+ }
+ void repositionDockable();
+ void drawToungue();
+ bool isDockVisible();
+
+ // gets a rect that bounds possible positions for a dockable control (EXT-1111)
+ void getAllowedRect(LLRect& rect);
+
+ S32 getTongueWidth() { return mDockTongue->getWidth(); }
+ S32 getTongueHeight() { return mDockTongue->getHeight(); }
+
+private:
+ virtual void moveDockable();
+private:
+ get_allowed_rect_callback_t mGetAllowedRectCallback;
+ bool mEnabled;
+ bool mRecalculateDocablePosition;
+ bool mDockWidgetVisible;
+ DocAt mDockAt;
+ LLView* mDockWidget;
+ LLRect mPrevDockRect;
+ LLRect mRootRect;
+ LLFloater* mDockableFloater;
+ LLUIImagePtr mDockTongue;
+ S32 mDockTongueX;
+ S32 mDockTongueY;
+};
+
+#endif /* LL_DOCKCONTROL_H */
diff --git a/indra/llui/lldraghandle.cpp b/indra/llui/lldraghandle.cpp
index 8ecbdb98e1..42e6c3c786 100644
--- a/indra/llui/lldraghandle.cpp
+++ b/indra/llui/lldraghandle.cpp
@@ -2,31 +2,25 @@
* @file lldraghandle.cpp
* @brief LLDragHandle base class
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -49,9 +43,9 @@
#include "lluictrlfactory.h"
const S32 LEADING_PAD = 5;
-const S32 TITLE_PAD = 8;
+const S32 TITLE_HPAD = 8;
const S32 BORDER_PAD = 1;
-const S32 LEFT_PAD = BORDER_PAD + TITLE_PAD + LEADING_PAD;
+const S32 LEFT_PAD = BORDER_PAD + TITLE_HPAD + LEADING_PAD;
const S32 RIGHT_PAD = BORDER_PAD + 32; // HACK: space for close btn and minimize btn
S32 LLDragHandle::sSnapMargin = 5;
@@ -108,10 +102,12 @@ void LLDragHandleTop::setTitle(const std::string& title)
LLTextBox::Params params;
params.name("Drag Handle Title");
params.rect(getRect());
- params.text(trimmed_title);
+ params.initial_value(trimmed_title);
params.font(font);
params.follows.flags(FOLLOWS_TOP | FOLLOWS_LEFT | FOLLOWS_RIGHT);
params.font_shadow(LLFontGL::DROP_SHADOW_SOFT);
+ params.use_ellipses = true;
+ params.parse_urls = false; //cancel URL replacement in floater title
mTitleBox = LLUICtrlFactory::create<LLTextBox> (params);
addChild( mTitleBox );
}
@@ -120,7 +116,7 @@ void LLDragHandleTop::setTitle(const std::string& title)
}
-const std::string& LLDragHandleTop::getTitle() const
+std::string LLDragHandleTop::getTitle() const
{
return mTitleBox == NULL ? LLStringUtil::null : mTitleBox->getText();
}
@@ -138,7 +134,7 @@ void LLDragHandleLeft::setTitle(const std::string& )
}
-const std::string& LLDragHandleLeft::getTitle() const
+std::string LLDragHandleLeft::getTitle() const
{
return LLStringUtil::null;
}
@@ -240,23 +236,24 @@ void LLDragHandleLeft::draw()
void LLDragHandleTop::reshapeTitleBox()
{
+ static LLUICachedControl<S32> title_vpad("UIFloaterTitleVPad", 0);
if( ! mTitleBox)
{
return;
}
const LLFontGL* font = LLFontGL::getFontSansSerif();
- S32 title_width = font->getWidth( mTitleBox->getText() ) + TITLE_PAD;
- 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() - BORDER_PAD,
- getRect().getWidth() - LEFT_PAD - RIGHT_PAD,
+ getRect().getHeight() - title_vpad,
+ title_width,
title_height);
- mTitleBox->setRect( title_rect );
+ // calls reshape on mTitleBox
+ mTitleBox->setShape( title_rect );
}
void LLDragHandleTop::reshape(S32 width, S32 height, BOOL called_from_parent)
@@ -317,6 +314,23 @@ BOOL LLDragHandle::handleHover(S32 x, S32 y, MASK mask)
S32 delta_x = screen_x - mDragLastScreenX;
S32 delta_y = screen_y - mDragLastScreenY;
+ // if dragging a docked floater we want to undock
+ if (((LLFloater*)getParent())->isDocked())
+ {
+ const S32 SLOP = 12;
+
+ if (delta_y <= -SLOP ||
+ delta_y >= SLOP)
+ {
+ ((LLFloater*)getParent())->setDocked(false, false);
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+ }
+
LLRect original_rect = getParent()->getRect();
LLRect translated_rect = getParent()->getRect();
translated_rect.translate(delta_x, delta_y);
diff --git a/indra/llui/lldraghandle.h b/indra/llui/lldraghandle.h
index 8b53c46ae9..7c56475e75 100644
--- a/indra/llui/lldraghandle.h
+++ b/indra/llui/lldraghandle.h
@@ -2,31 +2,25 @@
* @file lldraghandle.h
* @brief LLDragHandle base class
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -53,8 +47,9 @@ public:
Optional<LLUIColor> drag_shadow_color;
Params()
- : drag_highlight_color("", LLUI::getCachedColorFunctor("DefaultHighlightLight")),
- drag_shadow_color("", LLUI::getCachedColorFunctor("DefaultShadowDark"))
+ : label("label"),
+ drag_highlight_color("drag_highlight_color", LLUIColorTable::instance().getColor("DefaultHighlightLight")),
+ drag_shadow_color("drag_shadow_color", LLUIColorTable::instance().getColor("DefaultShadowDark"))
{
mouse_opaque(true);
follows.flags(FOLLOWS_ALL);
@@ -70,10 +65,12 @@ 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;
- virtual const std::string& getTitle() const = 0;
+ virtual std::string getTitle() const = 0;
virtual BOOL handleHover(S32 x, S32 y, MASK mask);
virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
@@ -87,6 +84,7 @@ protected:
LLTextBox* mTitleBox;
private:
+ LLRect mButtonsRect;
S32 mDragLastScreenX;
S32 mDragLastScreenY;
S32 mLastMouseScreenX;
@@ -111,7 +109,7 @@ protected:
friend class LLUICtrlFactory;
public:
virtual void setTitle( const std::string& title );
- virtual const std::string& getTitle() const;
+ virtual std::string getTitle() const;
virtual void draw();
virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
@@ -129,7 +127,7 @@ protected:
friend class LLUICtrlFactory;
public:
virtual void setTitle( const std::string& title );
- virtual const std::string& getTitle() const;
+ virtual std::string getTitle() const;
virtual void draw();
virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
diff --git a/indra/llui/lleditmenuhandler.cpp b/indra/llui/lleditmenuhandler.cpp
index 821afae8fd..d48237e377 100644
--- a/indra/llui/lleditmenuhandler.cpp
+++ b/indra/llui/lleditmenuhandler.cpp
@@ -2,31 +2,25 @@
* @file lleditmenuhandler.cpp
* @authors Aaron Yonas, James Cook
*
-* $LicenseInfo:firstyear=2006&license=viewergpl$
-*
-* Copyright (c) 2006-2009, Linden Research, Inc.
-*
+* $LicenseInfo:firstyear=2006&license=viewerlgpl$
* Second Life Viewer Source Code
-* The source code in this file ("Source Code") is provided by Linden Lab
-* to you under the terms of the GNU General Public License, version 2.0
-* ("GPL"), unless you have obtained a separate licensing agreement
-* ("Other License"), formally executed by you and Linden Lab. Terms of
-* the GPL can be found in doc/GPL-license.txt in this distribution, or
-* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+* Copyright (C) 2010, Linden Research, Inc.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation;
+* version 2.1 of the License only.
*
-* There are special exceptions to the terms and conditions of the GPL as
-* it is applied to this Source Code. View the full text of the exception
-* in the file doc/FLOSS-exception.txt in this software distribution, or
-* online at
-* http://secondlifegrid.net/programs/open_source/licensing/flossexception
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
*
-* 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.
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
-* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
-* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
-* COMPLETENESS OR PERFORMANCE.
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -37,3 +31,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..0932f094ef 100644
--- a/indra/llui/lleditmenuhandler.h
+++ b/indra/llui/lleditmenuhandler.h
@@ -2,31 +2,25 @@
* @file lleditmenuhandler.h
* @authors Aaron Yonas, James Cook
*
-* $LicenseInfo:firstyear=2006&license=viewergpl$
-*
-* Copyright (c) 2006-2009, Linden Research, Inc.
-*
+* $LicenseInfo:firstyear=2006&license=viewerlgpl$
* Second Life Viewer Source Code
-* The source code in this file ("Source Code") is provided by Linden Lab
-* to you under the terms of the GNU General Public License, version 2.0
-* ("GPL"), unless you have obtained a separate licensing agreement
-* ("Other License"), formally executed by you and Linden Lab. Terms of
-* the GPL can be found in doc/GPL-license.txt in this distribution, or
-* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+* Copyright (C) 2010, Linden Research, Inc.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation;
+* version 2.1 of the License only.
*
-* There are special exceptions to the terms and conditions of the GPL as
-* it is applied to this Source Code. View the full text of the exception
-* in the file doc/FLOSS-exception.txt in this software distribution, or
-* online at
-* http://secondlifegrid.net/programs/open_source/licensing/flossexception
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
*
-* 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.
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
-* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
-* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
-* COMPLETENESS OR PERFORMANCE.
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -38,7 +32,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/llf32uictrl.cpp b/indra/llui/llf32uictrl.cpp
index 0978005b78..d186f085b4 100644
--- a/indra/llui/llf32uictrl.cpp
+++ b/indra/llui/llf32uictrl.cpp
@@ -4,31 +4,25 @@
* @date 2008-09-08
* @brief Implementation for llf32uictrl.
*
- * $LicenseInfo:firstyear=2008&license=viewergpl$
- *
- * Copyright (c) 2008-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2008&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llui/llf32uictrl.h b/indra/llui/llf32uictrl.h
index 0a54fe761b..6e648f9102 100644
--- a/indra/llui/llf32uictrl.h
+++ b/indra/llui/llf32uictrl.h
@@ -4,31 +4,25 @@
* @date 2008-09-08
* @brief Base class for float-valued LLUICtrl widgets
*
- * $LicenseInfo:firstyear=2008&license=viewergpl$
- *
- * Copyright (c) 2008-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2008&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llui/llfiltereditor.cpp b/indra/llui/llfiltereditor.cpp
new file mode 100644
index 0000000000..d62874d793
--- /dev/null
+++ b/indra/llui/llfiltereditor.cpp
@@ -0,0 +1,46 @@
+/**
+ * @file llfiltereditor.cpp
+ * @brief LLFilterEditor implementation
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+// Text editor widget to let users enter a single line.
+
+#include "linden_common.h"
+
+#include "llfiltereditor.h"
+
+LLFilterEditor::LLFilterEditor(const LLFilterEditor::Params& p)
+: LLSearchEditor(p)
+{
+ setCommitOnFocusLost(FALSE); // we'll commit on every keystroke, don't re-commit when we take focus away (i.e. we go to interact with the actual results!)
+}
+
+
+void LLFilterEditor::handleKeystroke()
+{
+ this->LLSearchEditor::handleKeystroke();
+
+ // Commit on every keystroke.
+ onCommit();
+}
diff --git a/indra/llui/llfiltereditor.h b/indra/llui/llfiltereditor.h
new file mode 100644
index 0000000000..710699fdc1
--- /dev/null
+++ b/indra/llui/llfiltereditor.h
@@ -0,0 +1,59 @@
+/**
+ * @file llfiltereditor.h
+ * @brief Text editor widget that represents a filter operation
+ *
+ * Features:
+ * Text entry of a single line (text, delete, left and right arrow, insert, return).
+ * Callbacks either on every keystroke or just on the return key.
+ * Focus (allow multiple text entry widgets)
+ * Clipboard (cut, copy, and paste)
+ * Horizontal scrolling to allow strings longer than widget size allows
+ * Pre-validation (limit which keys can be used)
+ * Optional line history so previous entries can be recalled by CTRL UP/DOWN
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_FILTEREDITOR_H
+#define LL_FILTEREDITOR_H
+
+#include "llsearcheditor.h"
+
+class LLFilterEditor : public LLSearchEditor
+{
+public:
+ struct Params : public LLInitParam::Block<Params, LLSearchEditor::Params>
+ {
+ Params()
+ {
+ name = "filter_editor";
+ }
+ };
+
+protected:
+ LLFilterEditor(const Params&);
+ friend class LLUICtrlFactory;
+
+ /*virtual*/ void handleKeystroke();
+};
+
+#endif // LL_FILTEREDITOR_H
diff --git a/indra/llui/llflatlistview.cpp b/indra/llui/llflatlistview.cpp
new file mode 100644
index 0000000000..c57c02f4b1
--- /dev/null
+++ b/indra/llui/llflatlistview.cpp
@@ -0,0 +1,1342 @@
+/**
+ * @file llflatlistview.cpp
+ * @brief LLFlatListView base class and extension to support messages for several cases of an empty list.
+ *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llpanel.h"
+#include "lltextbox.h"
+
+#include "llflatlistview.h"
+
+static const LLDefaultChildRegistry::Register<LLFlatListView> flat_list_view("flat_list_view");
+
+const LLSD SELECTED_EVENT = LLSD().with("selected", true);
+const LLSD UNSELECTED_EVENT = LLSD().with("selected", false);
+
+//forward declaration
+bool llsds_are_equal(const LLSD& llsd_1, const LLSD& llsd_2);
+
+LLFlatListView::Params::Params()
+: item_pad("item_pad"),
+ allow_select("allow_select"),
+ multi_select("multi_select"),
+ keep_one_selected("keep_one_selected"),
+ keep_selection_visible_on_reshape("keep_selection_visible_on_reshape",false),
+ no_items_text("no_items_text")
+{};
+
+void LLFlatListView::reshape(S32 width, S32 height, BOOL called_from_parent /* = TRUE */)
+{
+ S32 delta = height - getRect().getHeight();
+ LLScrollContainer::reshape(width, height, called_from_parent);
+ setItemsNoScrollWidth(width);
+ rearrangeItems();
+
+ if(delta!= 0 && mKeepSelectionVisibleOnReshape)
+ {
+ ensureSelectedVisible();
+ }
+}
+
+const LLRect& LLFlatListView::getItemsRect() const
+{
+ return mItemsPanel->getRect();
+}
+
+bool LLFlatListView::addItem(LLPanel * item, const LLSD& value /*= LLUUID::null*/, EAddPosition pos /*= ADD_BOTTOM*/,bool rearrange /*= true*/)
+{
+ if (!item) return false;
+ if (value.isUndefined()) return false;
+
+ //force uniqueness of items, easiest check but unreliable
+ if (item->getParent() == mItemsPanel) return false;
+
+ item_pair_t* new_pair = new item_pair_t(item, value);
+ switch (pos)
+ {
+ case ADD_TOP:
+ mItemPairs.push_front(new_pair);
+ //in LLView::draw() children are iterated in backorder
+ mItemsPanel->addChildInBack(item);
+ break;
+ case ADD_BOTTOM:
+ mItemPairs.push_back(new_pair);
+ mItemsPanel->addChild(item);
+ break;
+ default:
+ LL_WARNS("") << "Unsupported position." << LL_ENDL;
+ delete new_pair;
+ return false;
+ break;
+ }
+
+ //_4 is for MASK
+ item->setMouseDownCallback(boost::bind(&LLFlatListView::onItemMouseClick, this, new_pair, _4));
+ item->setRightMouseDownCallback(boost::bind(&LLFlatListView::onItemRightMouseClick, this, new_pair, _4));
+
+ // Children don't accept the focus
+ item->setTabStop(false);
+
+ if (rearrange)
+ {
+ rearrangeItems();
+ notifyParentItemsRectChanged();
+ }
+ return true;
+}
+
+
+bool LLFlatListView::insertItemAfter(LLPanel* after_item, LLPanel* item_to_add, const LLSD& value /*= LLUUID::null*/)
+{
+ if (!after_item) return false;
+ if (!item_to_add) return false;
+ if (value.isUndefined()) return false;
+
+ if (mItemPairs.empty()) return false;
+
+ //force uniqueness of items, easiest check but unreliable
+ if (item_to_add->getParent() == mItemsPanel) return false;
+
+ item_pair_t* after_pair = getItemPair(after_item);
+ if (!after_pair) return false;
+
+ item_pair_t* new_pair = new item_pair_t(item_to_add, value);
+ if (after_pair == mItemPairs.back())
+ {
+ mItemPairs.push_back(new_pair);
+ mItemsPanel->addChild(item_to_add);
+ }
+ else
+ {
+ pairs_iterator_t it = mItemPairs.begin();
+ for (; it != mItemPairs.end(); ++it)
+ {
+ if (*it == after_pair)
+ {
+ // insert new elements before the element at position of passed iterator.
+ mItemPairs.insert(++it, new_pair);
+ mItemsPanel->addChild(item_to_add);
+ break;
+ }
+ }
+ }
+
+ //_4 is for MASK
+ item_to_add->setMouseDownCallback(boost::bind(&LLFlatListView::onItemMouseClick, this, new_pair, _4));
+ item_to_add->setRightMouseDownCallback(boost::bind(&LLFlatListView::onItemRightMouseClick, this, new_pair, _4));
+
+ rearrangeItems();
+ notifyParentItemsRectChanged();
+ return true;
+}
+
+
+bool LLFlatListView::removeItem(LLPanel* item, bool rearrange)
+{
+ if (!item) return false;
+ if (item->getParent() != mItemsPanel) return false;
+
+ item_pair_t* item_pair = getItemPair(item);
+ if (!item_pair) return false;
+
+ return removeItemPair(item_pair, rearrange);
+}
+
+bool LLFlatListView::removeItemByValue(const LLSD& value, bool rearrange)
+{
+ if (value.isUndefined()) return false;
+
+ item_pair_t* item_pair = getItemPair(value);
+ if (!item_pair) return false;
+
+ return removeItemPair(item_pair, rearrange);
+}
+
+bool LLFlatListView::removeItemByUUID(const LLUUID& uuid, bool rearrange)
+{
+ return removeItemByValue(LLSD(uuid), rearrange);
+}
+
+LLPanel* LLFlatListView::getItemByValue(const LLSD& value) const
+{
+ if (value.isUndefined()) return NULL;
+
+ item_pair_t* pair = getItemPair(value);
+ if (pair) return pair->first;
+ return NULL;
+}
+
+bool LLFlatListView::selectItem(LLPanel* item, bool select /*= true*/)
+{
+ if (!item) return false;
+ if (item->getParent() != mItemsPanel) return false;
+
+ item_pair_t* item_pair = getItemPair(item);
+ if (!item_pair) return false;
+
+ return selectItemPair(item_pair, select);
+}
+
+bool LLFlatListView::selectItemByValue(const LLSD& value, bool select /*= true*/)
+{
+ if (value.isUndefined()) return false;
+
+ item_pair_t* item_pair = getItemPair(value);
+ if (!item_pair) return false;
+
+ return selectItemPair(item_pair, select);
+}
+
+bool LLFlatListView::selectItemByUUID(const LLUUID& uuid, bool select /* = true*/)
+{
+ return selectItemByValue(LLSD(uuid), select);
+}
+
+
+LLSD LLFlatListView::getSelectedValue() const
+{
+ if (mSelectedItemPairs.empty()) return LLSD();
+
+ item_pair_t* first_selected_pair = mSelectedItemPairs.front();
+ return first_selected_pair->second;
+}
+
+void LLFlatListView::getSelectedValues(std::vector<LLSD>& selected_values) const
+{
+ if (mSelectedItemPairs.empty()) return;
+
+ for (pairs_const_iterator_t it = mSelectedItemPairs.begin(); it != mSelectedItemPairs.end(); ++it)
+ {
+ selected_values.push_back((*it)->second);
+ }
+}
+
+LLUUID LLFlatListView::getSelectedUUID() const
+{
+ const LLSD& value = getSelectedValue();
+ if (value.isDefined() && value.isUUID())
+ {
+ return value.asUUID();
+ }
+ else
+ {
+ return LLUUID::null;
+ }
+}
+
+void LLFlatListView::getSelectedUUIDs(uuid_vec_t& selected_uuids) const
+{
+ if (mSelectedItemPairs.empty()) return;
+
+ for (pairs_const_iterator_t it = mSelectedItemPairs.begin(); it != mSelectedItemPairs.end(); ++it)
+ {
+ selected_uuids.push_back((*it)->second.asUUID());
+ }
+}
+
+LLPanel* LLFlatListView::getSelectedItem() const
+{
+ if (mSelectedItemPairs.empty()) return NULL;
+
+ return mSelectedItemPairs.front()->first;
+}
+
+void LLFlatListView::getSelectedItems(std::vector<LLPanel*>& selected_items) const
+{
+ if (mSelectedItemPairs.empty()) return;
+
+ for (pairs_const_iterator_t it = mSelectedItemPairs.begin(); it != mSelectedItemPairs.end(); ++it)
+ {
+ selected_items.push_back((*it)->first);
+ }
+}
+
+void LLFlatListView::resetSelection(bool no_commit_on_deselection /*= false*/)
+{
+ if (mSelectedItemPairs.empty()) return;
+
+ for (pairs_iterator_t it= mSelectedItemPairs.begin(); it != mSelectedItemPairs.end(); ++it)
+ {
+ item_pair_t* pair_to_deselect = *it;
+ LLPanel* item = pair_to_deselect->first;
+ item->setValue(UNSELECTED_EVENT);
+ }
+
+ mSelectedItemPairs.clear();
+
+ if (mCommitOnSelectionChange && !no_commit_on_deselection)
+ {
+ onCommit();
+ }
+
+ // Stretch selected item rect to ensure it won't be clipped
+ mSelectedItemsBorder->setRect(getLastSelectedItemRect().stretch(-1));
+}
+
+void LLFlatListView::setNoItemsCommentText(const std::string& comment_text)
+{
+ mNoItemsCommentTextbox->setValue(comment_text);
+}
+
+U32 LLFlatListView::size(const bool only_visible_items) const
+{
+ if (only_visible_items)
+ {
+ U32 size = 0;
+ for (pairs_const_iterator_t
+ iter = mItemPairs.begin(),
+ iter_end = mItemPairs.end();
+ iter != iter_end; ++iter)
+ {
+ if ((*iter)->first->getVisible())
+ ++size;
+ }
+ return size;
+ }
+ else
+ {
+ return mItemPairs.size();
+ }
+}
+
+void LLFlatListView::clear()
+{
+ // This will clear mSelectedItemPairs, calling all appropriate callbacks.
+ resetSelection();
+
+ // do not use LLView::deleteAllChildren to avoid removing nonvisible items. drag-n-drop for ex.
+ for (pairs_iterator_t it = mItemPairs.begin(); it != mItemPairs.end(); ++it)
+ {
+ mItemsPanel->removeChild((*it)->first);
+ (*it)->first->die();
+ delete *it;
+ }
+ mItemPairs.clear();
+
+ // also set items panel height to zero. Reshape it to allow reshaping of non-item children
+ LLRect rc = mItemsPanel->getRect();
+ rc.mBottom = rc.mTop;
+ mItemsPanel->reshape(rc.getWidth(), rc.getHeight());
+ mItemsPanel->setRect(rc);
+
+ setNoItemsCommentVisible(true);
+ notifyParentItemsRectChanged();
+}
+
+void LLFlatListView::sort()
+{
+ if (!mItemComparator)
+ {
+ llwarns << "No comparator specified for sorting FlatListView items." << llendl;
+ return;
+ }
+
+ mItemPairs.sort(ComparatorAdaptor(*mItemComparator));
+ rearrangeItems();
+}
+
+bool LLFlatListView::updateValue(const LLSD& old_value, const LLSD& new_value)
+{
+ if (old_value.isUndefined() || new_value.isUndefined()) return false;
+ if (llsds_are_equal(old_value, new_value)) return false;
+
+ item_pair_t* item_pair = getItemPair(old_value);
+ if (!item_pair) return false;
+
+ item_pair->second = new_value;
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// PROTECTED STUFF
+//////////////////////////////////////////////////////////////////////////
+
+LLFlatListView::LLFlatListView(const LLFlatListView::Params& p)
+: LLScrollContainer(p)
+ , mItemComparator(NULL)
+ , mItemsPanel(NULL)
+ , mItemPad(p.item_pad)
+ , mAllowSelection(p.allow_select)
+ , mMultipleSelection(p.multi_select)
+ , mKeepOneItemSelected(p.keep_one_selected)
+ , mCommitOnSelectionChange(false)
+ , mPrevNotifyParentRect(LLRect())
+ , mNoItemsCommentTextbox(NULL)
+ , mIsConsecutiveSelection(false)
+ , mKeepSelectionVisibleOnReshape(p.keep_selection_visible_on_reshape)
+{
+ mBorderThickness = getBorderWidth();
+
+ LLRect scroll_rect = getRect();
+ LLRect items_rect;
+
+ setItemsNoScrollWidth(scroll_rect.getWidth());
+ items_rect.setLeftTopAndSize(mBorderThickness, scroll_rect.getHeight() - mBorderThickness, mItemsNoScrollWidth, 0);
+
+ LLPanel::Params pp;
+ pp.rect(items_rect);
+ mItemsPanel = LLUICtrlFactory::create<LLPanel> (pp);
+ addChild(mItemsPanel);
+
+ //we don't need to stretch in vertical direction on reshaping by a parent
+ //no bottom following!
+ mItemsPanel->setFollows(FOLLOWS_LEFT | FOLLOWS_RIGHT | FOLLOWS_TOP);
+
+ LLViewBorder::Params params;
+ params.name("scroll border");
+ 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
+void LLFlatListView::draw()
+{
+ // Highlight border if a child of this container has keyboard focus
+ if( mSelectedItemsBorder->getVisible() )
+ {
+ mSelectedItemsBorder->setKeyboardFocusHighlight( hasFocus() );
+ }
+ LLScrollContainer::draw();
+}
+
+// virtual
+BOOL LLFlatListView::postBuild()
+{
+ setTabStop(true);
+ return LLScrollContainer::postBuild();
+}
+
+void LLFlatListView::rearrangeItems()
+{
+ static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
+
+ setNoItemsCommentVisible(0==size());
+
+ if (mItemPairs.empty()) return;
+
+ //calculating required height - assuming items can be of different height
+ //list should accommodate all its items
+ S32 height = 0;
+
+ S32 invisible_children_count = 0;
+ pairs_iterator_t it = mItemPairs.begin();
+ for (; it != mItemPairs.end(); ++it)
+ {
+ LLPanel* item = (*it)->first;
+
+ // skip invisible child
+ if (!item->getVisible())
+ {
+ ++invisible_children_count;
+ continue;
+ }
+
+ height += item->getRect().getHeight();
+ }
+
+ // add paddings between items, excluding invisible ones
+ height += mItemPad * (mItemPairs.size() - invisible_children_count - 1);
+
+ LLRect rc = mItemsPanel->getRect();
+ S32 width = mItemsNoScrollWidth;
+
+ // update width to avoid horizontal scrollbar
+ if (height > getRect().getHeight() - 2 * mBorderThickness)
+ width -= scrollbar_size;
+
+ //changes the bottom, end of the list goes down in the scroll container
+ rc.setLeftTopAndSize(rc.mLeft, rc.mTop, width, height);
+ mItemsPanel->setRect(rc);
+
+ //reshaping items
+ S32 item_new_top = height;
+ pairs_iterator_t it2, first_it = mItemPairs.begin();
+ for (it2 = first_it; it2 != mItemPairs.end(); ++it2)
+ {
+ LLPanel* item = (*it2)->first;
+
+ // skip invisible child
+ if (!item->getVisible())
+ continue;
+
+ LLRect rc = item->getRect();
+ rc.setLeftTopAndSize(rc.mLeft, item_new_top, width, rc.getHeight());
+ item->reshape(rc.getWidth(), rc.getHeight());
+ item->setRect(rc);
+
+ // move top for next item in list
+ item_new_top -= (rc.getHeight() + mItemPad);
+ }
+
+ // Stretch selected item rect to ensure it won't be clipped
+ mSelectedItemsBorder->setRect(getLastSelectedItemRect().stretch(-1));
+}
+
+void LLFlatListView::onItemMouseClick(item_pair_t* item_pair, MASK mask)
+{
+ if (!item_pair) return;
+
+ if (!item_pair->first)
+ {
+ 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;
+ bool reverse = false;
+ pairs_list_t pairs_to_select;
+
+ // Pick out items from list between last selected and current clicked item_pair.
+ for (pairs_iterator_t
+ iter = mItemPairs.begin(),
+ iter_end = mItemPairs.end();
+ iter != iter_end; ++iter)
+ {
+ item_pair_t* cur = *iter;
+ if (cur == last_selected_pair || cur == item_pair)
+ {
+ // We've got reverse selection if last grabed item isn't a new selection.
+ reverse = grab_items && (cur != item_pair);
+ grab_items = !grab_items;
+ // Skip last selected and current clicked item pairs.
+ continue;
+ }
+ if (!cur->first->getVisible())
+ {
+ // Skip invisible item pairs.
+ continue;
+ }
+ if (grab_items)
+ {
+ pairs_to_select.push_back(cur);
+ }
+ }
+
+ if (reverse)
+ {
+ pairs_to_select.reverse();
+ }
+
+ pairs_to_select.push_back(item_pair);
+
+ for (pairs_iterator_t
+ iter = pairs_to_select.begin(),
+ iter_end = pairs_to_select.end();
+ iter != iter_end; ++iter)
+ {
+ item_pair_t* pair_to_select = *iter;
+ if (isSelected(pair_to_select))
+ {
+ // Item was already selected but there is a need to keep order from last selected pair to new selection.
+ // Do it here to prevent extra mCommitOnSelectionChange in selectItemPair().
+ mSelectedItemPairs.remove(pair_to_select);
+ mSelectedItemPairs.push_back(pair_to_select);
+ }
+ else
+ {
+ selectItemPair(pair_to_select, true);
+ }
+ }
+
+ if (!select_item)
+ {
+ // Update last selected item border.
+ mSelectedItemsBorder->setRect(getLastSelectedItemRect().stretch(-1));
+ }
+ return;
+ }
+
+ //no need to do additional commit on selection reset
+ if (!(mask & MASK_CONTROL) || !mMultipleSelection) resetSelection(true);
+
+ //only CTRL usage allows to deselect an item, usual clicking on an item cannot deselect it
+ if (mask & MASK_CONTROL)
+ selectItemPair(item_pair, select_item);
+ else
+ selectItemPair(item_pair, true);
+}
+
+void LLFlatListView::onItemRightMouseClick(item_pair_t* item_pair, MASK mask)
+{
+ if (!item_pair)
+ return;
+
+ // Forbid deselecting of items on right mouse button click if mMultipleSelection flag is set on,
+ // because some of derived classes may have context menu and selected items must be kept.
+ if ( !(mask & MASK_CONTROL) && mMultipleSelection && isSelected(item_pair) )
+ return;
+
+ // else got same behavior as at onItemMouseClick
+ onItemMouseClick(item_pair, mask);
+}
+
+BOOL LLFlatListView::handleKeyHere(KEY key, MASK mask)
+{
+ BOOL reset_selection = (mask != MASK_SHIFT);
+ BOOL handled = FALSE;
+ switch (key)
+ {
+ case KEY_RETURN:
+ {
+ if (mSelectedItemPairs.size() && mask == MASK_NONE)
+ {
+ mOnReturnSignal(this, getValue());
+ handled = TRUE;
+ }
+ break;
+ }
+ case KEY_UP:
+ {
+ if ( !selectNextItemPair(true, reset_selection) && reset_selection)
+ {
+ // If case we are in accordion tab notify parent to go to the previous accordion
+ if(notifyParent(LLSD().with("action","select_prev")) > 0 )//message was processed
+ resetSelection();
+ }
+ break;
+ }
+ case KEY_DOWN:
+ {
+ if ( !selectNextItemPair(false, reset_selection) && reset_selection)
+ {
+ // If case we are in accordion tab notify parent to go to the next accordion
+ if( notifyParent(LLSD().with("action","select_next")) > 0 ) //message was processed
+ resetSelection();
+ }
+ break;
+ }
+ case KEY_ESCAPE:
+ {
+ if (mask == MASK_NONE)
+ {
+ setFocus(FALSE); // pass focus to the game area (EXT-8357)
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ if ( ( key == KEY_UP || key == KEY_DOWN ) && mSelectedItemPairs.size() )
+ {
+ ensureSelectedVisible();
+ /*
+ LLRect visible_rc = getVisibleContentRect();
+ LLRect selected_rc = getLastSelectedItemRect();
+
+ if ( !visible_rc.contains (selected_rc) )
+ {
+ // But scroll in Items panel coordinates
+ scrollToShowRect(selected_rc);
+ }
+
+ // In case we are in accordion tab notify parent to show selected rectangle
+ LLRect screen_rc;
+ localRectToScreen(selected_rc, &screen_rc);
+ notifyParent(LLSD().with("scrollToShowRect",screen_rc.getValue()));*/
+
+ handled = TRUE;
+ }
+
+ return handled ? handled : LLScrollContainer::handleKeyHere(key, mask);
+}
+
+LLFlatListView::item_pair_t* LLFlatListView::getItemPair(LLPanel* item) const
+{
+ llassert(item);
+
+ for (pairs_const_iterator_t it= mItemPairs.begin(); it != mItemPairs.end(); ++it)
+ {
+ item_pair_t* item_pair = *it;
+ if (item_pair->first == item) return item_pair;
+ }
+ return NULL;
+}
+
+//compares two LLSD's
+bool llsds_are_equal(const LLSD& llsd_1, const LLSD& llsd_2)
+{
+ llassert(llsd_1.isDefined());
+ llassert(llsd_2.isDefined());
+
+ if (llsd_1.type() != llsd_2.type()) return false;
+
+ if (!llsd_1.isMap())
+ {
+ if (llsd_1.isUUID()) return llsd_1.asUUID() == llsd_2.asUUID();
+
+ //assumptions that string representaion is enough for other types
+ return llsd_1.asString() == llsd_2.asString();
+ }
+
+ if (llsd_1.size() != llsd_2.size()) return false;
+
+ LLSD::map_const_iterator llsd_1_it = llsd_1.beginMap();
+ LLSD::map_const_iterator llsd_2_it = llsd_2.beginMap();
+ for (S32 i = 0; i < llsd_1.size(); ++i)
+ {
+ if ((*llsd_1_it).first != (*llsd_2_it).first) return false;
+ if (!llsds_are_equal((*llsd_1_it).second, (*llsd_2_it).second)) return false;
+ ++llsd_1_it;
+ ++llsd_2_it;
+ }
+ return true;
+}
+
+LLFlatListView::item_pair_t* LLFlatListView::getItemPair(const LLSD& value) const
+{
+ llassert(value.isDefined());
+
+ for (pairs_const_iterator_t it= mItemPairs.begin(); it != mItemPairs.end(); ++it)
+ {
+ item_pair_t* item_pair = *it;
+ if (llsds_are_equal(item_pair->second, value)) return item_pair;
+ }
+ return NULL;
+}
+
+bool LLFlatListView::selectItemPair(item_pair_t* item_pair, bool select)
+{
+ llassert(item_pair);
+
+ if (!mAllowSelection && select) return false;
+
+ if (isSelected(item_pair) == select) return true; //already in specified selection state
+ if (select)
+ {
+ mSelectedItemPairs.push_back(item_pair);
+ }
+ else
+ {
+ mSelectedItemPairs.remove(item_pair);
+ }
+
+ //a way of notifying panel of selection state changes
+ LLPanel* item = item_pair->first;
+ item->setValue(select ? SELECTED_EVENT : UNSELECTED_EVENT);
+
+ if (mCommitOnSelectionChange)
+ {
+ onCommit();
+ }
+
+ // Stretch selected item rect to ensure it won't be clipped
+ mSelectedItemsBorder->setRect(getLastSelectedItemRect().stretch(-1));
+ // By default mark it as not consecutive selection
+ mIsConsecutiveSelection = false;
+
+ return true;
+}
+
+void LLFlatListView::scrollToShowFirstSelectedItem()
+{
+ if (!mSelectedItemPairs.size()) return;
+
+ LLRect selected_rc = mSelectedItemPairs.front()->first->getRect();
+
+ if (selected_rc.isValid())
+ {
+ scrollToShowRect(selected_rc);
+ }
+}
+
+LLRect LLFlatListView::getLastSelectedItemRect()
+{
+ if (!mSelectedItemPairs.size())
+ {
+ return LLRect::null;
+ }
+
+ return mSelectedItemPairs.back()->first->getRect();
+}
+
+void LLFlatListView::selectFirstItem ()
+{
+ // No items - no actions!
+ if (0 == size()) return;
+
+ // Select first visible item
+ for (pairs_iterator_t
+ iter = mItemPairs.begin(),
+ iter_end = mItemPairs.end();
+ iter != iter_end; ++iter)
+ {
+ // skip invisible items
+ if ( (*iter)->first->getVisible() )
+ {
+ selectItemPair(*iter, true);
+ ensureSelectedVisible();
+ break;
+ }
+ }
+}
+
+void LLFlatListView::selectLastItem ()
+{
+ // No items - no actions!
+ if (0 == size()) return;
+
+ // Select last visible item
+ for (pairs_list_t::reverse_iterator
+ r_iter = mItemPairs.rbegin(),
+ r_iter_end = mItemPairs.rend();
+ r_iter != r_iter_end; ++r_iter)
+ {
+ // skip invisible items
+ if ( (*r_iter)->first->getVisible() )
+ {
+ selectItemPair(*r_iter, true);
+ ensureSelectedVisible();
+ break;
+ }
+ }
+}
+
+void LLFlatListView::ensureSelectedVisible()
+{
+ LLRect selected_rc = getLastSelectedItemRect();
+
+ if ( selected_rc.isValid() )
+ {
+ scrollToShowRect(selected_rc);
+ }
+}
+
+
+// virtual
+bool LLFlatListView::selectNextItemPair(bool is_up_direction, bool reset_selection)
+{
+ // No items - no actions!
+ if ( 0 == size() )
+ return false;
+
+ if (!mIsConsecutiveSelection)
+ {
+ // Leave only one item selected if list has not consecutive selection
+ if (mSelectedItemPairs.size() && !reset_selection)
+ {
+ item_pair_t* cur_sel_pair = mSelectedItemPairs.back();
+ resetSelection();
+ selectItemPair (cur_sel_pair, true);
+ }
+ }
+
+ if ( mSelectedItemPairs.size() )
+ {
+ item_pair_t* to_sel_pair = NULL;
+ item_pair_t* cur_sel_pair = NULL;
+
+ // Take the last selected pair
+ cur_sel_pair = mSelectedItemPairs.back();
+ // Bases on given direction choose next item to select
+ if ( is_up_direction )
+ {
+ // Find current selected item position in mItemPairs list
+ pairs_list_t::reverse_iterator sel_it = std::find(mItemPairs.rbegin(), mItemPairs.rend(), cur_sel_pair);
+
+ for (;++sel_it != mItemPairs.rend();)
+ {
+ // skip invisible items
+ if ( (*sel_it)->first->getVisible() )
+ {
+ to_sel_pair = *sel_it;
+ break;
+ }
+ }
+ }
+ else
+ {
+ // Find current selected item position in mItemPairs list
+ pairs_list_t::iterator sel_it = std::find(mItemPairs.begin(), mItemPairs.end(), cur_sel_pair);
+
+ for (;++sel_it != mItemPairs.end();)
+ {
+ // skip invisible items
+ if ( (*sel_it)->first->getVisible() )
+ {
+ to_sel_pair = *sel_it;
+ break;
+ }
+ }
+ }
+
+ if ( to_sel_pair )
+ {
+ bool select = true;
+ if ( reset_selection )
+ {
+ // Reset current selection if we were asked about it
+ resetSelection();
+ }
+ else
+ {
+ // If item already selected and no reset request than we should deselect last selected item.
+ select = (mSelectedItemPairs.end() == std::find(mSelectedItemPairs.begin(), mSelectedItemPairs.end(), to_sel_pair));
+ }
+ // Select/Deselect next item
+ selectItemPair(select ? to_sel_pair : cur_sel_pair, select);
+ // Mark it as consecutive selection
+ mIsConsecutiveSelection = true;
+ return true;
+ }
+ }
+ else
+ {
+ // If there weren't selected items then choose the first one bases on given direction
+ // Force selection to first item
+ if (is_up_direction)
+ selectLastItem();
+ else
+ selectFirstItem();
+ // Mark it as consecutive selection
+ mIsConsecutiveSelection = true;
+ return true;
+ }
+
+ return false;
+}
+
+BOOL LLFlatListView::canSelectAll() const
+{
+ return 0 != size() && mAllowSelection && mMultipleSelection;
+}
+
+void LLFlatListView::selectAll()
+{
+ if (!mAllowSelection || !mMultipleSelection)
+ return;
+
+ mSelectedItemPairs.clear();
+
+ for (pairs_const_iterator_t it= mItemPairs.begin(); it != mItemPairs.end(); ++it)
+ {
+ item_pair_t* item_pair = *it;
+ mSelectedItemPairs.push_back(item_pair);
+ //a way of notifying panel of selection state changes
+ LLPanel* item = item_pair->first;
+ item->setValue(SELECTED_EVENT);
+ }
+
+ if (mCommitOnSelectionChange)
+ {
+ onCommit();
+ }
+
+ // Stretch selected item rect to ensure it won't be clipped
+ mSelectedItemsBorder->setRect(getLastSelectedItemRect().stretch(-1));
+}
+
+bool LLFlatListView::isSelected(item_pair_t* item_pair) const
+{
+ llassert(item_pair);
+
+ pairs_const_iterator_t it_end = mSelectedItemPairs.end();
+ return std::find(mSelectedItemPairs.begin(), it_end, item_pair) != it_end;
+}
+
+bool LLFlatListView::removeItemPair(item_pair_t* item_pair, bool rearrange)
+{
+ llassert(item_pair);
+
+ bool deleted = false;
+ bool selection_changed = false;
+ for (pairs_iterator_t it = mItemPairs.begin(); it != mItemPairs.end(); ++it)
+ {
+ item_pair_t* _item_pair = *it;
+ if (_item_pair == item_pair)
+ {
+ mItemPairs.erase(it);
+ deleted = true;
+ break;
+ }
+ }
+
+ if (!deleted) return false;
+
+ for (pairs_iterator_t it = mSelectedItemPairs.begin(); it != mSelectedItemPairs.end(); ++it)
+ {
+ item_pair_t* selected_item_pair = *it;
+ if (selected_item_pair == item_pair)
+ {
+ it = mSelectedItemPairs.erase(it);
+ selection_changed = true;
+ break;
+ }
+ }
+
+ mItemsPanel->removeChild(item_pair->first);
+ item_pair->first->die();
+ delete item_pair;
+
+ if (rearrange)
+ {
+ rearrangeItems();
+ notifyParentItemsRectChanged();
+ }
+
+ if (selection_changed && mCommitOnSelectionChange)
+ {
+ onCommit();
+ }
+
+ return true;
+}
+
+void LLFlatListView::notifyParentItemsRectChanged()
+{
+ S32 comment_height = 0;
+
+ // take into account comment text height if exists
+ if (mNoItemsCommentTextbox && mNoItemsCommentTextbox->getVisible())
+ {
+ // top text padding inside the textbox is included into the height
+ comment_height = mNoItemsCommentTextbox->getTextPixelHeight();
+
+ // take into account a distance from parent's top border to textbox's top
+ comment_height += getRect().getHeight() - mNoItemsCommentTextbox->getRect().mTop;
+ }
+
+ LLRect req_rect = getItemsRect();
+
+ // get maximum of items total height and comment text height
+ req_rect.setOriginAndSize(req_rect.mLeft, req_rect.mBottom, req_rect.getWidth(), llmax(req_rect.getHeight(), comment_height));
+
+ // take into account border size.
+ req_rect.stretch(getBorderWidth());
+
+ if (req_rect == mPrevNotifyParentRect)
+ return;
+
+ mPrevNotifyParentRect = req_rect;
+
+ LLSD params;
+ params["action"] = "size_changes";
+ params["width"] = req_rect.getWidth();
+ params["height"] = req_rect.getHeight();
+
+ if (getParent()) // dummy widgets don't have a parent
+ getParent()->notifyParent(params);
+}
+
+void LLFlatListView::setNoItemsCommentVisible(bool visible) const
+{
+ if (mNoItemsCommentTextbox)
+ {
+ mSelectedItemsBorder->setVisible(!visible);
+ mNoItemsCommentTextbox->setVisible(visible);
+ }
+}
+
+void LLFlatListView::getItems(std::vector<LLPanel*>& items) const
+{
+ if (mItemPairs.empty()) return;
+
+ items.clear();
+ for (pairs_const_iterator_t it = mItemPairs.begin(); it != mItemPairs.end(); ++it)
+ {
+ items.push_back((*it)->first);
+ }
+}
+
+void LLFlatListView::getValues(std::vector<LLSD>& values) const
+{
+ if (mItemPairs.empty()) return;
+
+ values.clear();
+ for (pairs_const_iterator_t it = mItemPairs.begin(); it != mItemPairs.end(); ++it)
+ {
+ values.push_back((*it)->second);
+ }
+}
+
+// virtual
+void LLFlatListView::onFocusReceived()
+{
+ if (size())
+ {
+ mSelectedItemsBorder->setVisible(TRUE);
+ }
+ gEditMenuHandler = this;
+}
+// virtual
+void LLFlatListView::onFocusLost()
+{
+ mSelectedItemsBorder->setVisible(FALSE);
+ // Route menu back to the default
+ if( gEditMenuHandler == this )
+ {
+ gEditMenuHandler = NULL;
+ }
+}
+
+//virtual
+S32 LLFlatListView::notify(const LLSD& info)
+{
+ if(info.has("action"))
+ {
+ std::string str_action = info["action"];
+ if(str_action == "select_first")
+ {
+ setFocus(true);
+ selectFirstItem();
+ return 1;
+ }
+ else if(str_action == "select_last")
+ {
+ setFocus(true);
+ selectLastItem();
+ return 1;
+ }
+ }
+ else if (info.has("rearrange"))
+ {
+ rearrangeItems();
+ notifyParentItemsRectChanged();
+ return 1;
+ }
+ return 0;
+}
+
+void LLFlatListView::detachItems(std::vector<LLPanel*>& detached_items)
+{
+ LLSD action;
+ action.with("detach", LLSD());
+ // Clear detached_items list
+ detached_items.clear();
+ // Go through items and detach valid items, remove them from items panel
+ // and add to detached_items.
+ for (pairs_iterator_t
+ iter = mItemPairs.begin(),
+ iter_end = mItemPairs.end();
+ iter != iter_end; ++iter)
+ {
+ LLPanel* pItem = (*iter)->first;
+ if (1 == pItem->notify(action))
+ {
+ selectItemPair((*iter), false);
+ mItemsPanel->removeChild(pItem);
+ detached_items.push_back(pItem);
+ }
+ }
+ if (!detached_items.empty())
+ {
+ // Some items were detached, clean ourself from unusable memory
+ if (detached_items.size() == mItemPairs.size())
+ {
+ // This way will be faster if all items were disconnected
+ for (pairs_iterator_t
+ iter = mItemPairs.begin(),
+ iter_end = mItemPairs.end();
+ iter != iter_end; ++iter)
+ {
+ (*iter)->first = NULL;
+ delete *iter;
+ }
+ mItemPairs.clear();
+ // Also set items panel height to zero.
+ // Reshape it to allow reshaping of non-item children.
+ LLRect rc = mItemsPanel->getRect();
+ rc.mBottom = rc.mTop;
+ mItemsPanel->reshape(rc.getWidth(), rc.getHeight());
+ mItemsPanel->setRect(rc);
+ setNoItemsCommentVisible(true);
+ }
+ else
+ {
+ for (std::vector<LLPanel*>::const_iterator
+ detached_iter = detached_items.begin(),
+ detached_iter_end = detached_items.end();
+ detached_iter != detached_iter_end; ++detached_iter)
+ {
+ LLPanel* pDetachedItem = *detached_iter;
+ for (pairs_iterator_t
+ iter = mItemPairs.begin(),
+ iter_end = mItemPairs.end();
+ iter != iter_end; ++iter)
+ {
+ item_pair_t* item_pair = *iter;
+ if (item_pair->first == pDetachedItem)
+ {
+ mItemPairs.erase(iter);
+ item_pair->first = NULL;
+ delete item_pair;
+ break;
+ }
+ }
+ }
+ rearrangeItems();
+ }
+ notifyParentItemsRectChanged();
+ }
+}
+
+
+/************************************************************************/
+/* LLFlatListViewEx implementation */
+/************************************************************************/
+LLFlatListViewEx::Params::Params()
+: no_items_msg("no_items_msg")
+, no_filtered_items_msg("no_filtered_items_msg")
+{
+
+}
+
+LLFlatListViewEx::LLFlatListViewEx(const Params& p)
+: LLFlatListView(p)
+, mNoFilteredItemsMsg(p.no_filtered_items_msg)
+, mNoItemsMsg(p.no_items_msg)
+, mForceShowingUnmatchedItems(false)
+, mHasMatchedItems(false)
+{
+
+}
+
+void LLFlatListViewEx::updateNoItemsMessage(const std::string& filter_string)
+{
+ bool items_filtered = !filter_string.empty();
+ if (items_filtered)
+ {
+ // items were filtered
+ LLStringUtil::format_map_t args;
+ args["[SEARCH_TERM]"] = LLURI::escape(filter_string);
+ std::string text = mNoFilteredItemsMsg;
+ LLStringUtil::format(text, args);
+ setNoItemsCommentText(text);
+ }
+ else
+ {
+ // list does not contain any items at all
+ setNoItemsCommentText(mNoItemsMsg);
+ }
+
+}
+
+bool LLFlatListViewEx::getForceShowingUnmatchedItems()
+{
+ return mForceShowingUnmatchedItems;
+}
+
+void LLFlatListViewEx::setForceShowingUnmatchedItems(bool show)
+{
+ mForceShowingUnmatchedItems = show;
+}
+
+void LLFlatListViewEx::setFilterSubString(const std::string& filter_str)
+{
+ if (0 != LLStringUtil::compareInsensitive(filter_str, mFilterSubString))
+ {
+ mFilterSubString = filter_str;
+ updateNoItemsMessage(mFilterSubString);
+ filterItems();
+ }
+}
+
+void LLFlatListViewEx::filterItems()
+{
+ typedef std::vector <LLPanel*> item_panel_list_t;
+
+ std::string cur_filter = mFilterSubString;
+ LLStringUtil::toUpper(cur_filter);
+
+ LLSD action;
+ action.with("match_filter", cur_filter);
+
+ item_panel_list_t items;
+ getItems(items);
+
+ mHasMatchedItems = false;
+ for (item_panel_list_t::iterator
+ iter = items.begin(),
+ iter_end = items.end();
+ iter != iter_end; ++iter)
+ {
+ LLPanel* pItem = (*iter);
+ // 0 signifies that filter is matched,
+ // i.e. we don't hide items that don't support 'match_filter' action, separators etc.
+ if (0 == pItem->notify(action))
+ {
+ mHasMatchedItems = true;
+ pItem->setVisible(true);
+ }
+ else
+ {
+ // TODO: implement (re)storing of current selection.
+ if(!mForceShowingUnmatchedItems)
+ {
+ selectItem(pItem, false);
+ }
+ pItem->setVisible(mForceShowingUnmatchedItems);
+ }
+ }
+
+ sort();
+ notifyParentItemsRectChanged();
+}
+
+bool LLFlatListViewEx::hasMatchedItems()
+{
+ return mHasMatchedItems;
+}
+
+//EOF
diff --git a/indra/llui/llflatlistview.h b/indra/llui/llflatlistview.h
new file mode 100644
index 0000000000..0515853698
--- /dev/null
+++ b/indra/llui/llflatlistview.h
@@ -0,0 +1,519 @@
+/**
+ * @file llflatlistview.h
+ * @brief LLFlatListView base class and extension to support messages for several cases of an empty list.
+ *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLFLATLISTVIEW_H
+#define LL_LLFLATLISTVIEW_H
+
+#include "llpanel.h"
+#include "llscrollcontainer.h"
+#include "lltextbox.h"
+
+
+/**
+ * LLFlatListView represents a flat list ui control that operates on items in a form of LLPanel's.
+ * LLSD can be associated with each added item, it can keep data from an item in digested form.
+ * Associated LLSD's can be of any type (singular, a map etc.).
+ * Items (LLPanel's subclasses) can be of different height.
+ * The list is LLPanel created in itself and grows in height while new items are added.
+ *
+ * The control can manage selection of its items when the flag "allow_select" is set. Also ability to select
+ * multiple items (by using CTRL) is enabled through setting the flag "multi_select" - if selection is not allowed that flag
+ * is ignored. The option "keep_one_selected" forces at least one item to be selected at any time (only for mouse events on items)
+ * since any item of the list was selected.
+ *
+ * Examples of using this control are presented in Picks panel (My Profile and Profile View), where this control is used to
+ * manage the list of pick items.
+ *
+ * ASSUMPTIONS AND STUFF
+ * - NULL pointers and undefined LLSD's are not accepted by any method of this class unless specified otherwise
+ * - Order of returned selected items are not guaranteed
+ * - The control assumes that all items being added are unique.
+ */
+class LLFlatListView : public LLScrollContainer, public LLEditMenuHandler
+{
+ LOG_CLASS(LLFlatListView);
+public:
+
+ /**
+ * Abstract comparator for comparing flat list items in a form of LLPanel
+ */
+ class ItemComparator
+ {
+ public:
+ ItemComparator() {};
+ virtual ~ItemComparator() {};
+
+ /** Returns true if item1 < item2, false otherwise */
+ virtual bool compare(const LLPanel* item1, const LLPanel* item2) const = 0;
+ };
+
+ /**
+ * Represents reverse comparator which acts as a decorator for a comparator that need to be reversed
+ */
+ class ItemReverseComparator : public ItemComparator
+ {
+ public:
+ ItemReverseComparator(const ItemComparator& comparator) : mComparator(comparator) {};
+ virtual ~ItemReverseComparator() {};
+
+ virtual bool compare(const LLPanel* item1, const LLPanel* item2) const
+ {
+ return mComparator.compare(item2, item1);
+ }
+
+ private:
+ const ItemComparator& mComparator;
+ };
+
+
+ struct Params : public LLInitParam::Block<Params, LLScrollContainer::Params>
+ {
+ /** turning on/off selection support */
+ Optional<bool> allow_select;
+
+ /** turning on/off multiple selection (works while clicking and holding CTRL)*/
+ Optional<bool> multi_select;
+
+ /** don't allow to deselect all selected items (for mouse events on items only) */
+ Optional<bool> keep_one_selected;
+
+ /** try to keep selection visible after reshape */
+ Optional<bool> keep_selection_visible_on_reshape;
+
+ /** padding between items */
+ Optional<U32> item_pad;
+
+ /** textbox with info message when list is empty*/
+ Optional<LLTextBox::Params> no_items_text;
+
+ Params();
+ };
+
+ // disable traversal when finding widget to hand focus off to
+ /*virtual*/ BOOL canFocusChildren() const { return FALSE; }
+
+ /**
+ * Connects callback to signal called when Return key is pressed.
+ */
+ boost::signals2::connection setReturnCallback( const commit_signal_t::slot_type& cb ) { return mOnReturnSignal.connect(cb); }
+
+ /** Overridden LLPanel's reshape, height is ignored, the list sets its height to accommodate all items */
+ virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
+
+ /** Returns full rect of child panel */
+ const LLRect& getItemsRect() const;
+
+ LLRect getRequiredRect() { return getItemsRect(); }
+
+ /** Returns distance between items */
+ const S32 getItemsPad() { return mItemPad; }
+
+ /**
+ * Adds and item and LLSD value associated with it to the list at specified position
+ * @return true if the item was added, false otherwise
+ */
+ virtual bool addItem(LLPanel * item, const LLSD& value = LLUUID::null, EAddPosition pos = ADD_BOTTOM, bool rearrange = true);
+
+ /**
+ * Insert item_to_add along with associated value to the list right after the after_item.
+ * @return true if the item was successfully added, false otherwise
+ */
+ virtual bool insertItemAfter(LLPanel* after_item, LLPanel* item_to_add, const LLSD& value = LLUUID::null);
+
+ /**
+ * Remove specified item
+ * @return true if the item was removed, false otherwise
+ */
+ virtual bool removeItem(LLPanel* item, bool rearrange = true);
+
+ /**
+ * Remove an item specified by value
+ * @return true if the item was removed, false otherwise
+ */
+ virtual bool removeItemByValue(const LLSD& value, bool rearrange = true);
+
+ /**
+ * Remove an item specified by uuid
+ * @return true if the item was removed, false otherwise
+ */
+ virtual bool removeItemByUUID(const LLUUID& uuid, bool rearrange = true);
+
+ /**
+ * Get an item by value
+ * @return the item as LLPanel if associated with value, NULL otherwise
+ */
+ virtual LLPanel* getItemByValue(const LLSD& value) const;
+
+ template<class T>
+ T* getTypedItemByValue(const LLSD& value) const
+ {
+ return dynamic_cast<T*>(getItemByValue(value));
+ }
+
+ /**
+ * Select or deselect specified item based on select
+ * @return true if succeed, false otherwise
+ */
+ virtual bool selectItem(LLPanel* item, bool select = true);
+
+ /**
+ * Select or deselect an item by associated value based on select
+ * @return true if succeed, false otherwise
+ */
+ virtual bool selectItemByValue(const LLSD& value, bool select = true);
+
+ /**
+ * Select or deselect an item by associated uuid based on select
+ * @return true if succeed, false otherwise
+ */
+ virtual bool selectItemByUUID(const LLUUID& uuid, bool select = true);
+
+ /**
+ * Get all panels stored in the list.
+ */
+ virtual void getItems(std::vector<LLPanel*>& items) const;
+
+ /**
+ * Get all items values.
+ */
+ virtual void getValues(std::vector<LLSD>& values) const;
+
+ /**
+ * Get LLSD associated with the first selected item
+ */
+ virtual LLSD getSelectedValue() const;
+
+ /**
+ * Get LLSD's associated with selected items.
+ * @param selected_values std::vector being populated with LLSD associated with selected items
+ */
+ virtual void getSelectedValues(std::vector<LLSD>& selected_values) const;
+
+
+ /**
+ * Get LLUUID associated with selected item
+ * @return LLUUID if such was associated with selected item
+ */
+ virtual LLUUID getSelectedUUID() const;
+
+ /**
+ * Get LLUUIDs associated with selected items
+ * @param selected_uuids An std::vector being populated with LLUUIDs associated with selected items
+ */
+ virtual void getSelectedUUIDs(uuid_vec_t& selected_uuids) const;
+
+ /** Get the top selected item */
+ virtual LLPanel* getSelectedItem() const;
+
+ /**
+ * Get selected items
+ * @param selected_items An std::vector being populated with pointers to selected items
+ */
+ virtual void getSelectedItems(std::vector<LLPanel*>& selected_items) const;
+
+
+ /**
+ * Resets selection of items.
+ *
+ * It calls onCommit callback if setCommitOnSelectionChange(bool b) was called with "true"
+ * argument for current Flat List.
+ * @param no_commit_on_deselection - if true onCommit callback will not be called
+ */
+ virtual void resetSelection(bool no_commit_on_deselection = false);
+
+ /**
+ * Sets comment text which will be shown in the list is it is empty.
+ *
+ * Textbox to hold passed text is created while this method is called at the first time.
+ *
+ * @param comment_text - string to be shown as a comment.
+ */
+ void setNoItemsCommentText( const std::string& comment_text);
+
+ /** Turn on/off multiple selection support */
+ void setAllowMultipleSelection(bool allow) { mMultipleSelection = allow; }
+
+ /** Turn on/off selection support */
+ void setAllowSelection(bool can_select) { mAllowSelection = can_select; }
+
+ /** Sets flag whether onCommit should be fired if selection was changed */
+ // FIXME: this should really be a separate signal, since "Commit" implies explicit user action, and selection changes can happen more indirectly.
+ void setCommitOnSelectionChange(bool b) { mCommitOnSelectionChange = b; }
+
+ /** Get number of selected items in the list */
+ U32 numSelected() const {return mSelectedItemPairs.size(); }
+
+ /** Get number of (visible) items in the list */
+ U32 size(const bool only_visible_items = true) const;
+
+ /** Removes all items from the list */
+ virtual void clear();
+
+ /**
+ * Removes all items that can be detached from the list but doesn't destroy
+ * them, caller responsible to manage items after they are detached.
+ * Detachable item should accept "detach" action via notify() method,
+ * where it disconnect all callbacks, does other valuable routines and
+ * return 1.
+ */
+ void detachItems(std::vector<LLPanel*>& detached_items);
+
+ /**
+ * Set comparator to use for future sorts.
+ *
+ * This class does NOT manage lifetime of the comparator
+ * but assumes that the comparator is always alive.
+ */
+ void setComparator(const ItemComparator* comp) { mItemComparator = comp; }
+ void sort();
+
+ bool updateValue(const LLSD& old_value, const LLSD& new_value);
+
+ void scrollToShowFirstSelectedItem();
+
+ void selectFirstItem ();
+ void selectLastItem ();
+
+ virtual S32 notify(const LLSD& info) ;
+
+protected:
+
+ /** Pairs LLpanel representing a single item LLPanel and LLSD associated with it */
+ typedef std::pair<LLPanel*, LLSD> item_pair_t;
+
+ typedef std::list<item_pair_t*> pairs_list_t;
+ typedef pairs_list_t::iterator pairs_iterator_t;
+ typedef pairs_list_t::const_iterator pairs_const_iterator_t;
+
+ /** An adapter for a ItemComparator */
+ struct ComparatorAdaptor
+ {
+ ComparatorAdaptor(const ItemComparator& comparator) : mComparator(comparator) {};
+
+ bool operator()(const item_pair_t* item_pair1, const item_pair_t* item_pair2)
+ {
+ return mComparator.compare(item_pair1->first, item_pair2->first);
+ }
+
+ const ItemComparator& mComparator;
+ };
+
+
+ friend class LLUICtrlFactory;
+ LLFlatListView(const LLFlatListView::Params& p);
+
+ /** Manage selection on mouse events */
+ void onItemMouseClick(item_pair_t* item_pair, MASK mask);
+
+ void onItemRightMouseClick(item_pair_t* item_pair, MASK mask);
+
+ /**
+ * Updates position of items.
+ * It does not take into account invisible items.
+ */
+ virtual void rearrangeItems();
+
+ virtual item_pair_t* getItemPair(LLPanel* item) const;
+
+ virtual item_pair_t* getItemPair(const LLSD& value) const;
+
+ virtual bool selectItemPair(item_pair_t* item_pair, bool select);
+
+ virtual bool selectNextItemPair(bool is_up_direction, bool reset_selection);
+
+ virtual BOOL canSelectAll() const;
+ virtual void selectAll();
+
+ virtual bool isSelected(item_pair_t* item_pair) const;
+
+ virtual bool removeItemPair(item_pair_t* item_pair, bool rearrange);
+
+ /**
+ * Notify parent about changed size of internal controls with "size_changes" action
+ *
+ * Size includes Items Rect width and either Items Rect height or comment text height.
+ * Comment text height is included if comment text is set and visible.
+ * List border size is also included into notified size.
+ */
+ void notifyParentItemsRectChanged();
+
+ virtual BOOL handleKeyHere(KEY key, MASK mask);
+
+ virtual BOOL postBuild();
+
+ virtual void onFocusReceived();
+
+ virtual void onFocusLost();
+
+ virtual void draw();
+
+ LLRect getLastSelectedItemRect();
+
+ void ensureSelectedVisible();
+
+private:
+
+ void setItemsNoScrollWidth(S32 new_width) {mItemsNoScrollWidth = new_width - 2 * mBorderThickness;}
+
+ void setNoItemsCommentVisible(bool visible) const;
+
+protected:
+
+ /** Comparator to use when sorting the list. */
+ const ItemComparator* mItemComparator;
+
+
+private:
+
+ LLPanel* mItemsPanel;
+
+ S32 mItemsNoScrollWidth;
+
+ S32 mBorderThickness;
+
+ /** Items padding */
+ S32 mItemPad;
+
+ /** Selection support flag */
+ bool mAllowSelection;
+
+ /** Multiselection support flag, ignored if selection is not supported */
+ bool mMultipleSelection;
+
+ /**
+ * Flag specified whether onCommit be called if selection is changed in the list.
+ *
+ * Can be ignored in the resetSelection() method.
+ * @see resetSelection()
+ */
+ bool mCommitOnSelectionChange;
+
+ bool mKeepOneItemSelected;
+
+ bool mIsConsecutiveSelection;
+
+ bool mKeepSelectionVisibleOnReshape;
+
+ /** All pairs of the list */
+ pairs_list_t mItemPairs;
+
+ /** Selected pairs for faster access */
+ pairs_list_t mSelectedItemPairs;
+
+ /**
+ * Rectangle contained previous size of items parent notified last time.
+ * Is used to reduce amount of parentNotify() calls if size was not changed.
+ */
+ LLRect mPrevNotifyParentRect;
+
+ LLTextBox* mNoItemsCommentTextbox;
+
+ LLViewBorder* mSelectedItemsBorder;
+
+ commit_signal_t mOnReturnSignal;
+};
+
+/**
+ * Extends LLFlatListView functionality to show different messages when there are no items in the
+ * list depend on whether they are filtered or not.
+ *
+ * Class provides one message per case of empty list.
+ * It also provides protected updateNoItemsMessage() method to be called each time when derived list
+ * is changed to update base mNoItemsCommentTextbox value.
+ *
+ * It is implemented to avoid duplication of this functionality in concrete implementations of the
+ * lists. It is intended to be used as a base class for lists which should support two different
+ * messages for empty state. Can be improved to support more than two messages via state-to-message map.
+ */
+class LLFlatListViewEx : public LLFlatListView
+{
+ LOG_CLASS(LLFlatListViewEx);
+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; }
+
+ bool getForceShowingUnmatchedItems();
+
+ void setForceShowingUnmatchedItems(bool show);
+
+ /**
+ * Sets up new filter string and filters the list.
+ */
+ void setFilterSubString(const std::string& filter_str);
+
+ /**
+ * Filters the list, rearranges and notifies parent about shape changes.
+ * Derived classes may want to overload rearrangeItems() to exclude repeated separators after filtration.
+ */
+ void filterItems();
+
+ /**
+ * Returns true if last call of filterItems() found at least one matching item
+ */
+ bool hasMatchedItems();
+
+protected:
+ LLFlatListViewEx(const Params& p);
+
+ /**
+ * Applies a message for empty list depend on passed argument.
+ *
+ * @param filter_string - if is not empty, message for filtered items will be set, otherwise for
+ * completely empty list. Value of filter string will be passed as search_term in SLURL.
+ */
+ void updateNoItemsMessage(const std::string& filter_string);
+
+private:
+ std::string mNoFilteredItemsMsg;
+ std::string mNoItemsMsg;
+ std::string mFilterSubString;
+ /**
+ * Show list items that don't match current filter
+ */
+ bool mForceShowingUnmatchedItems;
+ /**
+ * True if last call of filterItems() found at least one matching item
+ */
+ bool mHasMatchedItems;
+};
+
+#endif
diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp
index a14b99eeb7..eb5d7a6b6a 100644
--- a/indra/llui/llfloater.cpp
+++ b/indra/llui/llfloater.cpp
@@ -2,31 +2,25 @@
* @file llfloater.cpp
* @brief LLFloater base class
*
- * $LicenseInfo:firstyear=2002&license=viewergpl$
- *
- * Copyright (c) 2002-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -59,61 +53,38 @@
#include "lltabcontainer.h"
#include "v2math.h"
#include "lltrans.h"
+#include "llhelp.h"
#include "llmultifloater.h"
+#include "llsdutil.h"
// use this to control "jumping" behavior when Ctrl-Tabbing
const S32 TABBED_FLOATER_OFFSET = 0;
-std::string LLFloater::sButtonActiveImageNames[BUTTON_COUNT] =
-{
- "closebox.tga", //BUTTON_CLOSE
- "restore.tga", //BUTTON_RESTORE
- "minimize.tga", //BUTTON_MINIMIZE
- "tearoffbox.tga", //BUTTON_TEAR_OFF
- "closebox.tga", //BUTTON_EDIT
-};
-
-std::string LLFloater::sButtonInactiveImageNames[BUTTON_COUNT] =
-{
- "close_inactive_blue.tga", //BUTTON_CLOSE
- "restore_inactive.tga", //BUTTON_RESTORE
- "minimize_inactive.tga", //BUTTON_MINIMIZE
- "tearoffbox.tga", //BUTTON_TEAR_OFF
- "close_inactive_blue.tga", //BUTTON_EDIT
-};
-
-std::string LLFloater::sButtonPressedImageNames[BUTTON_COUNT] =
-{
- "close_in_blue.tga", //BUTTON_CLOSE
- "restore_pressed.tga", //BUTTON_RESTORE
- "minimize_pressed.tga", //BUTTON_MINIMIZE
- "tearoff_pressed.tga", //BUTTON_TEAR_OFF
- "close_in_blue.tga", //BUTTON_EDIT
-};
std::string LLFloater::sButtonNames[BUTTON_COUNT] =
{
- "llfloater_close_btn", //BUTTON_CLOSE
+ "llfloater_close_btn", //BUTTON_CLOSE
"llfloater_restore_btn", //BUTTON_RESTORE
"llfloater_minimize_btn", //BUTTON_MINIMIZE
"llfloater_tear_off_btn", //BUTTON_TEAR_OFF
- "llfloater_edit_btn", //BUTTON_EDIT
+ "llfloater_dock_btn", //BUTTON_DOCK
+ "llfloater_help_btn" //BUTTON_HELP
};
-std::string LLFloater::sButtonToolTips[BUTTON_COUNT] = {};
-
+std::string LLFloater::sButtonToolTips[BUTTON_COUNT];
std::string LLFloater::sButtonToolTipsIndex[BUTTON_COUNT]=
{
#ifdef LL_DARWIN
- "BUTTON_CLOSE_DARWIN",//LLTrans::getString("BUTTON_CLOSE_DARWIN"), //"Close (Cmd-W)", //BUTTON_CLOSE
+ "BUTTON_CLOSE_DARWIN", //"Close (Cmd-W)", //BUTTON_CLOSE
#else
- "BUTTON_CLOSE_WIN", //LLTrans::getString("BUTTON_CLOSE_WIN"), //"Close (Ctrl-W)", //BUTTON_CLOSE
+ "BUTTON_CLOSE_WIN", //"Close (Ctrl-W)", //BUTTON_CLOSE
#endif
- "BUTTON_RESTORE",//LLTrans::getString("BUTTON_RESTORE"), //"Restore", //BUTTON_RESTORE
- "BUTTON_MINIMIZE",//LLTrans::getString("BUTTON_MINIMIZE"), //"Minimize", //BUTTON_MINIMIZE
- "BUTTON_TEAR_OFF",//LLTrans::getString("BUTTON_TEAR_OFF"), //"Tear Off", //BUTTON_TEAR_OFF
- "BUTTON_EDIT", //LLTrans::getString("BUTTON_EDIT"), // "Edit", //BUTTON_EDIT
+ "BUTTON_RESTORE", //"Restore", //BUTTON_RESTORE
+ "BUTTON_MINIMIZE", //"Minimize", //BUTTON_MINIMIZE
+ "BUTTON_TEAR_OFF", //"Tear Off", //BUTTON_TEAR_OFF
+ "BUTTON_DOCK",
+ "BUTTON_HELP"
};
LLFloater::click_callback LLFloater::sButtonCallbacks[BUTTON_COUNT] =
@@ -122,15 +93,26 @@ LLFloater::click_callback LLFloater::sButtonCallbacks[BUTTON_COUNT] =
LLFloater::onClickMinimize, //BUTTON_RESTORE
LLFloater::onClickMinimize, //BUTTON_MINIMIZE
LLFloater::onClickTearOff, //BUTTON_TEAR_OFF
- LLFloater::onClickEdit, //BUTTON_EDIT
+ LLFloater::onClickDock, //BUTTON_DOCK
+ LLFloater::onClickHelp //BUTTON_HELP
};
LLMultiFloater* LLFloater::sHostp = NULL;
-BOOL LLFloater::sEditModeEnabled;
+BOOL LLFloater::sQuitting = FALSE; // Flag to prevent storing visibility controls while quitting
LLFloater::handle_map_t LLFloater::sFloaterMap;
LLFloaterView* gFloaterView = NULL;
+/*==========================================================================*|
+// DEV-38598: The fundamental problem with this operation is that it can only
+// support a subset of LLSD values. While it's plausible to compare two arrays
+// lexicographically, what strict ordering can you impose on maps?
+// (LLFloaterTOS's current key is an LLSD map.)
+
+// Of course something like this is necessary if you want to build a std::set
+// or std::map with LLSD keys. Fortunately we're getting by with other
+// container types for now.
+
//static
bool LLFloater::KeyCompare::compare(const LLSD& a, const LLSD& b)
{
@@ -158,80 +140,102 @@ bool LLFloater::KeyCompare::compare(const LLSD& a, const LLSD& b)
else
return false; // no valid operation for Binary
}
+|*==========================================================================*/
bool LLFloater::KeyCompare::equate(const LLSD& a, const LLSD& b)
{
- if (a.type() != b.type())
- {
- //llerrs << "Mismatched LLSD types: (" << a << ") mismatches (" << b << ")" << llendl;
- return false;
- }
- else if (a.isUndefined())
- return true;
- else if (a.isInteger())
- return a.asInteger() == b.asInteger();
- else if (a.isReal())
- return a.asReal() == b.asReal();
- else if (a.isString())
- return a.asString() == b.asString();
- else if (a.isUUID())
- return a.asUUID() == b.asUUID();
- else if (a.isDate())
- return a.asDate() == b.asDate();
- else if (a.isURI())
- return a.asString() == b.asString(); // compare URIs as strings
- else if (a.isBoolean())
- return a.asBoolean() == b.asBoolean();
- else
- return false; // no valid operation for Binary
+ return llsd_equals(a, b);
}
//************************************
-LLFloater::LLFloater(const LLSD& key, const LLFloater::Params& p)
- : LLPanel(),
- mDragHandle(NULL),
- mTitle(p.title),
- mShortTitle(p.short_title),
- mSingleInstance(p.single_instance),
- mKey(key),
- mAutoTile(p.auto_tile),
- mCanTearOff(p.can_tear_off),
- mCanMinimize(p.can_minimize),
- mCanClose(p.can_close),
- mDragOnLeft(p.can_drag_on_left),
- mResizable(p.can_resize),
- mMinWidth(p.min_width),
- mMinHeight(p.min_height),
- mMinimized(FALSE),
- mForeground(FALSE),
- mFirstLook(TRUE),
- mEditing(FALSE),
- mButtonScale(1.0f),
- mAutoFocus(TRUE), // automatically take focus when opened
- mHasBeenDraggedWhileMinimized(FALSE),
- mPreviousMinimizedBottom(0),
- mPreviousMinimizedLeft(0),
- mNotificationContext(NULL)
-{
- static LLUICachedControl<LLColor4> default_background_color ("FloaterDefaultBackgroundColor", *(new LLColor4));
- static LLUICachedControl<LLColor4> focus_background_color ("FloaterFocusBackgroundColor", *(new LLColor4));
-
+LLFloater::Params::Params()
+: title("title"),
+ short_title("short_title"),
+ single_instance("single_instance", false),
+ auto_tile("auto_tile", false),
+ can_resize("can_resize", false),
+ can_minimize("can_minimize", true),
+ can_close("can_close", true),
+ can_drag_on_left("can_drag_on_left", false),
+ can_tear_off("can_tear_off", true),
+ save_dock_state("save_dock_state", false),
+ save_rect("save_rect", false),
+ save_visibility("save_visibility", false),
+ can_dock("can_dock", false),
+ open_centered("open_centered", false),
+ header_height("header_height", 0),
+ legacy_header_height("legacy_header_height", 0),
+ close_image("close_image"),
+ restore_image("restore_image"),
+ minimize_image("minimize_image"),
+ tear_off_image("tear_off_image"),
+ dock_image("dock_image"),
+ help_image("help_image"),
+ close_pressed_image("close_pressed_image"),
+ restore_pressed_image("restore_pressed_image"),
+ minimize_pressed_image("minimize_pressed_image"),
+ tear_off_pressed_image("tear_off_pressed_image"),
+ dock_pressed_image("dock_pressed_image"),
+ help_pressed_image("help_pressed_image"),
+ open_callback("open_callback"),
+ close_callback("close_callback")
+{
+ visible = false;
+}
+
+
+//static
+const LLFloater::Params& LLFloater::getDefaultParams()
+{
+ return LLUICtrlFactory::getDefaultParams<LLFloater>();
+}
+
+//static
+void LLFloater::initClass()
+{
+ // translate tooltips for floater buttons
for (S32 i = 0; i < BUTTON_COUNT; i++)
{
- sButtonToolTips[i] =LLTrans::getString( sButtonToolTipsIndex[i]);
+ sButtonToolTips[i] = LLTrans::getString( sButtonToolTipsIndex[i] );
}
-
- mHandle.bind(this);
- mNotificationContext = new LLFloaterNotificationContext(getHandle());
- mBgColorAlpha = default_background_color;
- mBgColorOpaque = focus_background_color;
+}
- for (S32 i = 0; i < 4; i++)
- {
- mResizeBar[i] = NULL;
- mResizeHandle[i] = NULL;
- }
+// defaults for floater param block pulled from widgets/floater.xml
+static LLWidgetNameRegistry::StaticRegistrar sRegisterFloaterParams(&typeid(LLFloater::Params), "floater");
+
+LLFloater::LLFloater(const LLSD& key, const LLFloater::Params& p)
+: LLPanel(), // intentionally do not pass params here, see initFromParams
+ mDragHandle(NULL),
+ mTitle(p.title),
+ mShortTitle(p.short_title),
+ mSingleInstance(p.single_instance),
+ mKey(key),
+ mAutoTile(p.auto_tile),
+ mCanTearOff(p.can_tear_off),
+ mCanMinimize(p.can_minimize),
+ mCanClose(p.can_close),
+ mDragOnLeft(p.can_drag_on_left),
+ mResizable(p.can_resize),
+ mMinWidth(p.min_width),
+ mMinHeight(p.min_height),
+ mHeaderHeight(p.header_height),
+ mLegacyHeaderHeight(p.legacy_header_height),
+ mMinimized(FALSE),
+ mForeground(FALSE),
+ mFirstLook(TRUE),
+ mButtonScale(1.0f),
+ mAutoFocus(TRUE), // automatically take focus when opened
+ mCanDock(false),
+ mDocked(false),
+ mTornOff(false),
+ mHasBeenDraggedWhileMinimized(FALSE),
+ mPreviousMinimizedBottom(0),
+ mPreviousMinimizedLeft(0)
+// mNotificationContext(NULL)
+{
+ mHandle.bind(this);
+// mNotificationContext = new LLFloaterNotificationContext(getHandle());
// Clicks stop here.
setMouseOpaque(TRUE);
@@ -243,45 +247,47 @@ LLFloater::LLFloater(const LLSD& key, const LLFloater::Params& p)
// prior rectangle to be used on restore.
mExpandedRect.set(0,0,0,0);
- for (S32 i = 0; i < BUTTON_COUNT; i++)
- {
- mButtonsEnabled[i] = FALSE;
- mButtons[i] = NULL;
- }
- for (S32 i = 0; i < 4; i++)
- {
- mResizeBar[i] = NULL;
- mResizeHandle[i] = NULL;
- }
+ memset(mButtonsEnabled, 0, BUTTON_COUNT * sizeof(bool));
+ memset(mButtons, 0, BUTTON_COUNT * sizeof(LLButton*));
+
+ addDragHandle();
+ addResizeCtrls();
initFromParams(p);
// chrome floaters don't take focus at all
setFocusRoot(!getIsChrome());
- initFloater();
+ initFloater(p);
}
// Note: Floaters constructed from XML call init() twice!
-void LLFloater::initFloater()
+void LLFloater::initFloater(const Params& p)
{
- addDragHandle();
-
- addResizeCtrls();
-
// Close button.
if (mCanClose)
{
mButtonsEnabled[BUTTON_CLOSE] = TRUE;
}
+ // Help button: '?'
+ if ( !mHelpTopic.empty() )
+ {
+ mButtonsEnabled[BUTTON_HELP] = TRUE;
+ }
+
// Minimize button only for top draggers
if ( !mDragOnLeft && mCanMinimize )
{
mButtonsEnabled[BUTTON_MINIMIZE] = TRUE;
}
- buildButtons();
+ if(mCanDock)
+ {
+ mButtonsEnabled[BUTTON_DOCK] = TRUE;
+ }
+
+ buildButtons(p);
// Floaters are created in the invisible state
setVisible(FALSE);
@@ -297,9 +303,6 @@ void LLFloater::initFloater()
void LLFloater::addDragHandle()
{
- static LLUICachedControl<S32> floater_close_box_size ("UIFloaterCloseBoxSize", 0);
- S32 close_box_size = mCanClose ? floater_close_box_size : 0;
-
if (!mDragHandle)
{
if (mDragOnLeft)
@@ -320,6 +323,15 @@ void LLFloater::addDragHandle()
}
addChild(mDragHandle);
}
+ layoutDragHandle();
+ applyTitle();
+}
+
+void LLFloater::layoutDragHandle()
+{
+ static LLUICachedControl<S32> floater_close_box_size ("UIFloaterCloseBoxSize", 0);
+ S32 close_box_size = mCanClose ? floater_close_box_size : 0;
+
LLRect rect;
if (mDragOnLeft)
{
@@ -329,46 +341,22 @@ void LLFloater::addDragHandle()
{
rect = getLocalRect();
}
- mDragHandle->setRect(rect);
- updateButtons();
- applyTitle();
+ mDragHandle->setShape(rect);
+ updateTitleButtons();
}
void LLFloater::addResizeCtrls()
-{
- for (S32 i = 0; i < 4; i++)
- {
- if (mResizeBar[i])
- {
- removeChild(mResizeBar[i]);
- delete mResizeBar[i];
- mResizeBar[i] = NULL;
- }
- if (mResizeHandle[i])
- {
- removeChild(mResizeHandle[i]);
- delete mResizeHandle[i];
- mResizeHandle[i] = NULL;
- }
- }
- if( !mResizable )
- {
- return;
- }
-
+{
// Resize bars (sides)
- const S32 RESIZE_BAR_THICKNESS = 3;
LLResizeBar::Params p;
p.name("resizebar_left");
p.resizing_view(this);
- p.rect(LLRect( 0, getRect().getHeight(), RESIZE_BAR_THICKNESS, 0));
p.min_size(mMinWidth);
p.side(LLResizeBar::LEFT);
mResizeBar[LLResizeBar::LEFT] = LLUICtrlFactory::create<LLResizeBar>(p);
addChild( mResizeBar[LLResizeBar::LEFT] );
p.name("resizebar_top");
- p.rect(LLRect( 0, getRect().getHeight(), getRect().getWidth(), getRect().getHeight() - RESIZE_BAR_THICKNESS));
p.min_size(mMinHeight);
p.side(LLResizeBar::TOP);
@@ -376,15 +364,12 @@ void LLFloater::addResizeCtrls()
addChild( mResizeBar[LLResizeBar::TOP] );
p.name("resizebar_right");
- p.rect(LLRect(getRect().getWidth() - RESIZE_BAR_THICKNESS, getRect().getHeight(), getRect().getWidth(), 0));
p.min_size(mMinWidth);
- p.side(LLResizeBar::RIGHT);
-
+ p.side(LLResizeBar::RIGHT);
mResizeBar[LLResizeBar::RIGHT] = LLUICtrlFactory::create<LLResizeBar>(p);
addChild( mResizeBar[LLResizeBar::RIGHT] );
p.name("resizebar_bottom");
- p.rect(LLRect(0, RESIZE_BAR_THICKNESS, getRect().getWidth(), 0));
p.min_size(mMinHeight);
p.side(LLResizeBar::BOTTOM);
mResizeBar[LLResizeBar::BOTTOM] = LLUICtrlFactory::create<LLResizeBar>(p);
@@ -392,27 +377,80 @@ void LLFloater::addResizeCtrls()
// Resize handles (corners)
LLResizeHandle::Params handle_p;
- handle_p.rect(LLRect( getRect().getWidth() - RESIZE_HANDLE_WIDTH, RESIZE_HANDLE_HEIGHT, getRect().getWidth(), 0));
+ // handles must not be mouse-opaque, otherwise they block hover events
+ // to other buttons like the close box. JC
+ handle_p.mouse_opaque(false);
handle_p.min_width(mMinWidth);
handle_p.min_height(mMinHeight);
handle_p.corner(LLResizeHandle::RIGHT_BOTTOM);
mResizeHandle[0] = LLUICtrlFactory::create<LLResizeHandle>(handle_p);
addChild(mResizeHandle[0]);
- handle_p.rect(LLRect( getRect().getWidth() - RESIZE_HANDLE_WIDTH, getRect().getHeight(), getRect().getWidth(), getRect().getHeight() - RESIZE_HANDLE_HEIGHT));
handle_p.corner(LLResizeHandle::RIGHT_TOP);
mResizeHandle[1] = LLUICtrlFactory::create<LLResizeHandle>(handle_p);
addChild(mResizeHandle[1]);
- handle_p.rect(LLRect( 0, RESIZE_HANDLE_HEIGHT, RESIZE_HANDLE_WIDTH, 0 ));
handle_p.corner(LLResizeHandle::LEFT_BOTTOM);
mResizeHandle[2] = LLUICtrlFactory::create<LLResizeHandle>(handle_p);
addChild(mResizeHandle[2]);
- handle_p.rect(LLRect( 0, getRect().getHeight(), RESIZE_HANDLE_WIDTH, getRect().getHeight() - RESIZE_HANDLE_HEIGHT ));
handle_p.corner(LLResizeHandle::LEFT_TOP);
mResizeHandle[3] = LLUICtrlFactory::create<LLResizeHandle>(handle_p);
addChild(mResizeHandle[3]);
+
+ layoutResizeCtrls();
+}
+
+void LLFloater::layoutResizeCtrls()
+{
+ LLRect rect;
+
+ // Resize bars (sides)
+ const S32 RESIZE_BAR_THICKNESS = 3;
+ rect = LLRect( 0, getRect().getHeight(), RESIZE_BAR_THICKNESS, 0);
+ mResizeBar[LLResizeBar::LEFT]->setRect(rect);
+
+ rect = LLRect( 0, getRect().getHeight(), getRect().getWidth(), getRect().getHeight() - RESIZE_BAR_THICKNESS);
+ mResizeBar[LLResizeBar::TOP]->setRect(rect);
+
+ rect = LLRect(getRect().getWidth() - RESIZE_BAR_THICKNESS, getRect().getHeight(), getRect().getWidth(), 0);
+ mResizeBar[LLResizeBar::RIGHT]->setRect(rect);
+
+ rect = LLRect(0, RESIZE_BAR_THICKNESS, getRect().getWidth(), 0);
+ mResizeBar[LLResizeBar::BOTTOM]->setRect(rect);
+
+ // Resize handles (corners)
+ rect = LLRect( getRect().getWidth() - RESIZE_HANDLE_WIDTH, RESIZE_HANDLE_HEIGHT, getRect().getWidth(), 0);
+ mResizeHandle[0]->setRect(rect);
+
+ rect = LLRect( getRect().getWidth() - RESIZE_HANDLE_WIDTH, getRect().getHeight(), getRect().getWidth(), getRect().getHeight() - RESIZE_HANDLE_HEIGHT);
+ mResizeHandle[1]->setRect(rect);
+
+ rect = LLRect( 0, RESIZE_HANDLE_HEIGHT, RESIZE_HANDLE_WIDTH, 0 );
+ mResizeHandle[2]->setRect(rect);
+
+ rect = LLRect( 0, getRect().getHeight(), RESIZE_HANDLE_WIDTH, getRect().getHeight() - RESIZE_HANDLE_HEIGHT );
+ mResizeHandle[3]->setRect(rect);
+}
+
+void LLFloater::enableResizeCtrls(bool enable)
+{
+ for (S32 i = 0; i < 4; ++i)
+ {
+ mResizeBar[i]->setVisible(enable);
+ mResizeBar[i]->setEnabled(enable);
+
+ mResizeHandle[i]->setVisible(enable);
+ mResizeHandle[i]->setEnabled(enable);
+ }
+}
+
+void LLFloater::destroy()
+{
+ // LLFloaterReg should be synchronized with "dead" floater to avoid returning dead instance before
+ // it was deleted via LLMortician::updateClass(). See EXT-8458.
+ LLFloaterReg::removeInstance(mInstanceName, mKey);
+ die();
}
// virtual
@@ -420,8 +458,8 @@ LLFloater::~LLFloater()
{
LLFloaterReg::removeInstance(mInstanceName, mKey);
- delete mNotificationContext;
- mNotificationContext = NULL;
+// delete mNotificationContext;
+// mNotificationContext = NULL;
//// am I not hosted by another floater?
//if (mHostHandle.isDead())
@@ -454,27 +492,74 @@ LLFloater::~LLFloater()
storeRectControl();
setVisible(false); // We're not visible if we're destroyed
storeVisibilityControl();
+ storeDockStateControl();
}
void LLFloater::storeRectControl()
{
if( mRectControl.size() > 1 )
{
- LLUI::sSettingGroups["floater"]->setRect( mRectControl, getRect() );
+ getControlGroup()->setRect( mRectControl, getRect() );
}
}
void LLFloater::storeVisibilityControl()
{
- if( mVisibilityControl.size() > 1 )
+ if( !sQuitting && mVisibilityControl.size() > 1 )
+ {
+ getControlGroup()->setBOOL( mVisibilityControl, getVisible() );
+ }
+}
+
+void LLFloater::storeDockStateControl()
+{
+ if( !sQuitting && mDocStateControl.size() > 1 )
{
- LLUI::sSettingGroups["floater"]->setBOOL( mVisibilityControl, getVisible() );
+ getControlGroup()->setBOOL( mDocStateControl, isDocked() );
}
}
+LLRect LLFloater::getSavedRect() const
+{
+ LLRect rect;
+
+ if (mRectControl.size() > 1)
+ {
+ rect = getControlGroup()->getRect(mRectControl);
+ }
+
+ return rect;
+}
+
+bool LLFloater::hasSavedRect() const
+{
+ return !getSavedRect().isEmpty();
+}
+
+// static
+std::string LLFloater::getControlName(const std::string& name, const LLSD& key)
+{
+ std::string ctrl_name = name;
+
+ // Add the key to the control name if appropriate.
+ if (key.isString() && !key.asString().empty())
+ {
+ ctrl_name += "_" + key.asString();
+ }
+
+ return ctrl_name;
+}
+
+// static
+LLControlGroup* LLFloater::getControlGroup()
+{
+ // Floater size, position, visibility, etc are saved in per-account settings.
+ return LLUI::sSettingGroups["account"];
+}
+
void LLFloater::setVisible( BOOL visible )
{
- LLPanel::setVisible(visible);
+ LLPanel::setVisible(visible); // calls handleVisibilityChange()
if( visible && mFirstLook )
{
mFirstLook = FALSE;
@@ -482,10 +567,7 @@ void LLFloater::setVisible( BOOL visible )
if( !visible )
{
- if( gFocusMgr.childIsTopCtrl( this ) )
- {
- gFocusMgr.setTopCtrl(NULL);
- }
+ LLUI::removePopup(this);
if( gFocusMgr.childHasMouseCapture( this ) )
{
@@ -509,18 +591,19 @@ void LLFloater::setVisible( BOOL visible )
}
// virtual
-void LLFloater::onVisibilityChange ( BOOL new_visibility )
+void LLFloater::handleVisibilityChange ( BOOL new_visibility )
{
if (new_visibility)
{
if (getHost())
getHost()->setFloaterFlashing(this, FALSE);
}
- LLPanel::onVisibilityChange ( new_visibility );
+ LLPanel::handleVisibilityChange ( 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
@@ -553,11 +636,20 @@ void LLFloater::openFloater(const LLSD& key)
setVisibleAndFrontmost(mAutoFocus);
}
+ mOpenSignal(this, key);
onOpen(key);
+
+ dirtyRect();
}
void LLFloater::closeFloater(bool app_quitting)
{
+ llinfos << "Closing floater " << getName() << llendl;
+ if (app_quitting)
+ {
+ LLFloater::sQuitting = true;
+ }
+
// Always unminimize before trying to close.
// Most of the time the user will never see this state.
setMinimized(FALSE);
@@ -614,9 +706,31 @@ void LLFloater::closeFloater(bool app_quitting)
}
}
}
-
- // Let floater do cleanup.
+
+ dirtyRect();
+
+ // Close callbacks
onClose(app_quitting);
+ mCloseSignal(this, LLSD(app_quitting));
+
+ // Hide or Destroy
+ if (mSingleInstance)
+ {
+ // Hide the instance
+ if (getHost())
+ {
+ getHost()->setVisible(FALSE);
+ }
+ else
+ {
+ setVisible(FALSE);
+ }
+ }
+ else
+ {
+ setVisible(FALSE); // hide before destroying (so handleVisibilityChange() gets called)
+ destroy();
+ }
}
}
@@ -629,15 +743,9 @@ void LLFloater::reshape(S32 width, S32 height, BOOL called_from_parent)
void LLFloater::releaseFocus()
{
- if( gFocusMgr.childIsTopCtrl( this ) )
- {
- gFocusMgr.setTopCtrl(NULL);
- }
+ LLUI::removePopup(this);
- if( gFocusMgr.childHasKeyboardFocus( this ) )
- {
- gFocusMgr.setKeyboardFocus(NULL);
- }
+ setFocus(FALSE);
if( gFocusMgr.childHasMouseCapture( this ) )
{
@@ -687,11 +795,24 @@ LLMultiFloater* LLFloater::getHost()
return (LLMultiFloater*)mHostHandle.get();
}
+void LLFloater::applySavedVariables()
+{
+ applyRectControl();
+ applyDockState();
+}
+
void LLFloater::applyRectControl()
{
+ // first, center on screen if requested
+ if (mOpenCentered)
+ {
+ center();
+ }
+
+ // override center if we have saved rect control
if (mRectControl.size() > 1)
{
- const LLRect& rect = LLUI::sSettingGroups["floater"]->getRect(mRectControl);
+ const LLRect& rect = getControlGroup()->getRect(mRectControl);
if (rect.getWidth() > 0 && rect.getHeight() > 0)
{
translate( rect.mLeft - getRect().mLeft, rect.mBottom - getRect().mBottom);
@@ -703,13 +824,18 @@ void LLFloater::applyRectControl()
}
}
-void LLFloater::applyTitle()
+void LLFloater::applyDockState()
{
- if (gNoRender)
+ if (mDocStateControl.size() > 1)
{
- return;
+ bool dockState = getControlGroup()->getBOOL(mDocStateControl);
+ setDocked(dockState);
}
+}
+
+void LLFloater::applyTitle()
+{
if (!mDragHandle)
{
return;
@@ -723,9 +849,14 @@ void LLFloater::applyTitle()
{
mDragHandle->setTitle ( mTitle );
}
+
+ if (getHost())
+ {
+ getHost()->updateFloaterTitle(this);
+ }
}
-const std::string& LLFloater::getCurrentTitle() const
+std::string LLFloater::getCurrentTitle() const
{
return mDragHandle ? mDragHandle->getTitle() : LLStringUtil::null;
}
@@ -736,7 +867,7 @@ void LLFloater::setTitle( const std::string& title )
applyTitle();
}
-std::string LLFloater::getTitle()
+std::string LLFloater::getTitle() const
{
if (mTitle.empty())
{
@@ -754,7 +885,7 @@ void LLFloater::setShortTitle( const std::string& short_title )
applyTitle();
}
-std::string LLFloater::getShortTitle()
+std::string LLFloater::getShortTitle() const
{
if (mShortTitle.empty())
{
@@ -766,8 +897,6 @@ std::string LLFloater::getShortTitle()
}
}
-
-
BOOL LLFloater::canSnapTo(const LLView* other_view)
{
if (NULL == other_view)
@@ -798,9 +927,11 @@ void LLFloater::setSnappedTo(const LLView* snap_view)
else
{
//RN: assume it's a floater as it must be a sibling to our parent floater
- LLFloater* floaterp = (LLFloater*)snap_view;
-
- setSnapTarget(floaterp->getHandle());
+ const LLFloater* floaterp = dynamic_cast<const LLFloater*>(snap_view);
+ if (floaterp)
+ {
+ setSnapTarget(floaterp->getHandle());
+ }
}
}
@@ -860,13 +991,17 @@ void LLFloater::handleReshape(const LLRect& new_rect, bool by_user)
void LLFloater::setMinimized(BOOL minimize)
{
- static LLUICachedControl<S32> floater_header_size ("UIFloaterHeaderSize", 0);
+ const LLFloater::Params& default_params = LLFloater::getDefaultParams();
+ S32 floater_header_size = default_params.header_height;
static LLUICachedControl<S32> minimized_width ("UIMinimizedWidth", 0);
if (minimize == mMinimized) return;
if (minimize)
{
+ // minimized flag should be turned on before release focus
+ mMinimized = TRUE;
+
mExpandedRect = getRect();
// If the floater has been dragged while minimized in the
@@ -928,8 +1063,6 @@ void LLFloater::setMinimized(BOOL minimize)
}
}
- mMinimized = TRUE;
-
// Reshape *after* setting mMinimized
reshape( minimized_width, floater_header_size, TRUE);
}
@@ -982,11 +1115,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 )
@@ -997,7 +1129,7 @@ void LLFloater::setFocus( BOOL b )
}
LLUICtrl* last_focus = gFocusMgr.getLastFocusForGroup(this);
// a descendent already has focus
- BOOL child_had_focus = gFocusMgr.childHasKeyboardFocus(this);
+ BOOL child_had_focus = hasFocus();
// give focus to first valid descendent
LLPanel::setFocus(b);
@@ -1028,7 +1160,8 @@ void LLFloater::setFocus( BOOL b )
void LLFloater::setRect(const LLRect &rect)
{
LLPanel::setRect(rect);
- addDragHandle(); // re-add drag handle, sized based on rect
+ layoutDragHandle();
+ layoutResizeCtrls();
}
// virtual
@@ -1041,6 +1174,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
@@ -1110,7 +1244,7 @@ void LLFloater::setHost(LLMultiFloater* host)
mButtonScale = 1.f;
//mButtonsEnabled[BUTTON_TEAR_OFF] = FALSE;
}
- updateButtons();
+ updateTitleButtons();
if (host)
{
mHostHandle = host->getHandle();
@@ -1179,7 +1313,7 @@ void LLFloater::removeDependentFloater(LLFloater* floaterp)
floaterp->mDependeeHandle = LLHandle<LLFloater>();
}
-BOOL LLFloater::offerClickToButton(S32 x, S32 y, MASK mask, EFloaterButtons index)
+BOOL LLFloater::offerClickToButton(S32 x, S32 y, MASK mask, EFloaterButton index)
{
if( mButtonsEnabled[index] )
{
@@ -1198,6 +1332,12 @@ BOOL LLFloater::offerClickToButton(S32 x, S32 y, MASK mask, EFloaterButtons inde
return FALSE;
}
+BOOL LLFloater::handleScrollWheel(S32 x, S32 y, S32 clicks)
+{
+ LLPanel::handleScrollWheel(x,y,clicks);
+ return TRUE;//always
+}
+
// virtual
BOOL LLFloater::handleMouseDown(S32 x, S32 y, MASK mask)
{
@@ -1289,27 +1429,35 @@ void LLFloater::setFrontmost(BOOL take_focus)
}
}
-//static
-void LLFloater::setEditModeEnabled(BOOL enable)
+void LLFloater::setCanDock(bool b)
{
- if (enable != sEditModeEnabled)
+ if(b != mCanDock)
{
- S32 count = 0;
- for(handle_map_iter_t iter = sFloaterMap.begin(); iter != sFloaterMap.end(); ++iter)
+ mCanDock = b;
+ if(mCanDock)
{
- LLFloater* floater = iter->second;
- if (!floater->isDead())
- {
- iter->second->mButtonsEnabled[BUTTON_EDIT] = enable;
- iter->second->updateButtons();
- }
- count++;
+ mButtonsEnabled[BUTTON_DOCK] = !mDocked;
+ }
+ else
+ {
+ mButtonsEnabled[BUTTON_DOCK] = FALSE;
}
}
-
- sEditModeEnabled = enable;
+ updateTitleButtons();
}
+void LLFloater::setDocked(bool docked, bool pop_on_undock)
+{
+ if(docked != mDocked && mCanDock)
+ {
+ mDocked = docked;
+ mButtonsEnabled[BUTTON_DOCK] = !mDocked;
+ updateTitleButtons();
+
+ storeDockStateControl();
+ }
+
+}
// static
void LLFloater::onClickMinimize(LLFloater* self)
@@ -1321,9 +1469,9 @@ void LLFloater::onClickMinimize(LLFloater* self)
void LLFloater::onClickTearOff(LLFloater* self)
{
- static LLUICachedControl<S32> floater_header_size ("UIFloaterHeaderSize", 0);
if (!self)
return;
+ S32 floater_header_size = self->mHeaderHeight;
LLMultiFloater* host_floater = self->getHost();
if (host_floater) //Tear off
{
@@ -1343,6 +1491,7 @@ void LLFloater::onClickTearOff(LLFloater* self)
gFloaterView->adjustToFitScreen(self, FALSE);
// give focus to new window to keep continuity for the user
self->setFocus(TRUE);
+ self->setTornOff(true);
}
else //Attach to parent.
{
@@ -1354,15 +1503,32 @@ void LLFloater::onClickTearOff(LLFloater* self)
// make sure host is visible
new_host->openFloater(new_host->getKey());
}
+ self->setTornOff(false);
}
+ self->updateTitleButtons();
}
// static
-void LLFloater::onClickEdit(LLFloater* self)
+void LLFloater::onClickDock(LLFloater* self)
{
- if (!self)
- return;
- self->mEditing = self->mEditing ? FALSE : TRUE;
+ if(self && self->mCanDock)
+ {
+ self->setDocked(!self->mDocked, true);
+ }
+}
+
+// static
+void LLFloater::onClickHelp( LLFloater* self )
+{
+ if (self && LLUI::sHelpImpl)
+ {
+ // find the current help context for this floater
+ std::string help_topic;
+ if (self->findHelpTopic(help_topic))
+ {
+ LLUI::sHelpImpl->showTopic(help_topic);
+ }
+ }
}
// static
@@ -1388,7 +1554,8 @@ LLFloater* LLFloater::getClosableFloaterFromFocus()
// The focused floater may not be closable,
// Find and close a parental floater that is closeable, if any.
- for(LLFloater* floater_to_close = focused_floater;
+ LLFloater* prev_floater = NULL;
+ for(LLFloater* floater_to_close = focused_floater;
NULL != floater_to_close;
floater_to_close = gFloaterView->getParentFloater(floater_to_close))
{
@@ -1396,6 +1563,14 @@ LLFloater* LLFloater::getClosableFloaterFromFocus()
{
return floater_to_close;
}
+
+ // If floater has as parent root view
+ // gFloaterView->getParentFloater(floater_to_close) returns
+ // the same floater_to_close, so we need to check this.
+ if (prev_floater == floater_to_close) {
+ break;
+ }
+ prev_floater = floater_to_close;
}
return NULL;
@@ -1426,53 +1601,68 @@ void LLFloater::onClickClose( LLFloater* self )
{
if (!self)
return;
- self->closeFloater(false);
+ self->onClickCloseBtn();
+}
+
+void LLFloater::onClickCloseBtn()
+{
+ closeFloater(false);
}
// virtual
void LLFloater::draw()
{
+ F32 alpha = getDrawContext().mAlpha;
// 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 LLUICachedControl<LLColor4> shadow_color_cached ("ColorDropShadow", *(new LLColor4));
- 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,
- llround(shadow_offset));
-
- // No transparent windows in simple UI
+ LLUIImage* image = NULL;
+ LLColor4 color;
+ LLColor4 overlay_color;
if (isBackgroundOpaque())
{
- gl_rect_2d( left, top, right, bottom, mBgColorOpaque );
+ // NOTE: image may not be set
+ image = getBackgroundImage();
+ color = getBackgroundColor();
+ overlay_color = getBackgroundImageOverlay();
}
else
{
- gl_rect_2d( left, top, right, bottom, mBgColorAlpha );
+ image = getTransparentImage();
+ color = getTransparentColor();
+ overlay_color = getTransparentImageOverlay();
}
- if(gFocusMgr.childHasKeyboardFocus(this) && !getIsChrome() && !getCurrentTitle().empty())
+ if (image)
+ {
+ // We're using images for this floater's backgrounds
+ image->draw(getLocalRect(), overlay_color % alpha);
+ }
+ else
{
- static LLUICachedControl<LLColor4> titlebar_focus_color ("TitleBarFocusColor", *(new LLColor4));
+ // We're not using images, use old-school flat colors
+ gl_rect_2d( left, top, right, bottom, color % alpha );
+
// draw highlight on title bar to indicate focus. RDW
- const LLFontGL* font = LLFontGL::getFontSansSerif();
- LLRect r = getRect();
- gl_rect_2d_offset_local(0, r.getHeight(), r.getWidth(), r.getHeight() - (S32)font->getLineHeight() - 1,
- titlebar_focus_color, 0, TRUE);
+ if(hasFocus()
+ && !getIsChrome()
+ && !getCurrentTitle().empty())
+ {
+ static LLUIColor titlebar_focus_color = LLUIColorTable::instance().getColor("TitleBarFocusColor");
+
+ const LLFontGL* font = LLFontGL::getFontSansSerif();
+ LLRect r = getRect();
+ gl_rect_2d_offset_local(0, r.getHeight(), r.getWidth(), r.getHeight() - (S32)font->getLineHeight() - 1,
+ titlebar_focus_color % alpha, 0, TRUE);
+ }
}
}
@@ -1482,9 +1672,9 @@ void LLFloater::draw()
{
if (hasFocus() && getDefaultButton()->getEnabled())
{
- LLUICtrl* focus_ctrl = gFocusMgr.getKeyboardFocus();
+ LLFocusableElement* focus_ctrl = gFocusMgr.getKeyboardFocus();
// is this button a direct descendent and not a nested widget (e.g. checkbox)?
- BOOL focus_is_child_button = dynamic_cast<LLButton*>(focus_ctrl) != NULL && focus_ctrl->getParent() == this;
+ BOOL focus_is_child_button = dynamic_cast<LLButton*>(focus_ctrl) != NULL && dynamic_cast<LLButton*>(focus_ctrl)->getParent() == this;
// only enable default button when current focus is not a button
getDefaultButton()->setBorderEnabled(!focus_is_child_button);
}
@@ -1503,35 +1693,8 @@ void LLFloater::draw()
}
else
{
- // draw children
- LLView* focused_child = 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);
- }
-
- if( isBackgroundVisible() )
- {
- // add in a border to improve spacialized visual aclarity ;)
- // use lines instead of gl_rect_2d so we can round the edges as per james' recommendation
- static LLUICachedControl<LLColor4> focus_border_color ("FloaterFocusBorderColor", *(new LLColor4));
- static LLUICachedControl<LLColor4> unfocus_border_color ("FloaterUnfocusBorderColor", *(new LLColor4));
- LLUI::setLineWidth(1.5f);
- LLColor4 outlineColor = gFocusMgr.childHasKeyboardFocus(this) ? focus_border_color() : unfocus_border_color;
- gl_rect_2d_offset_local(0, getRect().getHeight() + 1, getRect().getWidth() + 1, 0, outlineColor, -LLPANEL_BORDER_WIDTH, FALSE);
- LLUI::setLineWidth(1.f);
}
// update tearoff button for torn off floaters
@@ -1546,6 +1709,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,
@@ -1559,7 +1745,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)
@@ -1567,7 +1753,7 @@ void LLFloater::setCanClose(BOOL can_close)
mCanClose = can_close;
mButtonsEnabled[BUTTON_CLOSE] = can_close;
- updateButtons();
+ updateTitleButtons();
}
void LLFloater::setCanTearOff(BOOL can_tear_off)
@@ -1575,14 +1761,14 @@ void LLFloater::setCanTearOff(BOOL can_tear_off)
mCanTearOff = can_tear_off;
mButtonsEnabled[BUTTON_TEAR_OFF] = mCanTearOff && !mHostHandle.isDead();
- updateButtons();
+ updateTitleButtons();
}
void LLFloater::setCanResize(BOOL can_resize)
{
mResizable = can_resize;
- addResizeCtrls();
+ enableResizeCtrls(can_resize);
}
void LLFloater::setCanDrag(BOOL can_drag)
@@ -1599,21 +1785,40 @@ 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++)
{
- if(!mButtons[i]) continue;
- mButtons[i]->setEnabled(mButtonsEnabled[i]);
+ if (!mButtons[i])
+ {
+ continue;
+ }
+
+ bool enabled = mButtonsEnabled[i];
+ if (i == BUTTON_HELP)
+ {
+ // don't show the help button if the floater is minimized
+ // or if it is a docked tear-off floater
+ if (isMinimized() || (mButtonsEnabled[BUTTON_TEAR_OFF] && ! mTornOff))
+ {
+ enabled = false;
+ }
+ }
+ if (i == BUTTON_CLOSE && mButtonScale != 1.f)
+ {
+ //*HACK: always render close button for hosted floaters so
+ //that users don't accidentally hit the button when
+ //closing multiple windows in the chatterbox
+ enabled = true;
+ }
- if (mButtonsEnabled[i]
- //*HACK: always render close button for hosted floaters
- // so that users don't accidentally hit the button when closing multiple windows
- // in the chatterbox
- || (i == BUTTON_CLOSE && mButtonScale != 1.f))
+ mButtons[i]->setEnabled(enabled);
+
+ if (enabled)
{
button_count++;
@@ -1635,21 +1840,36 @@ 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
mButtons[i]->setTabStop(i == BUTTON_RESTORE);
}
- else if (mButtons[i])
+ else
{
mButtons[i]->setVisible(FALSE);
}
}
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()
+void LLFloater::buildButtons(const Params& floater_params)
{
static LLUICachedControl<S32> floater_close_box_size ("UIFloaterCloseBoxSize", 0);
static LLUICachedControl<S32> close_box_from_top ("UICloseBoxFromTop", 0);
@@ -1683,34 +1903,97 @@ void LLFloater::buildButtons()
LLButton::Params p;
p.name(sButtonNames[i]);
p.rect(btn_rect);
- p.label("");
- p.image_unselected.name(sButtonActiveImageNames[i]);
- p.image_selected.name(sButtonPressedImageNames[i]);
- p.image_hover_selected.name(sButtonPressedImageNames[i]);
- p.image_hover_unselected.name(sButtonPressedImageNames[i]);
+ p.image_unselected = getButtonImage(floater_params, (EFloaterButton)i);
+ // Selected, no matter if hovered or not, is "pressed"
+ LLUIImage* pressed_image = getButtonPressedImage(floater_params, (EFloaterButton)i);
+ p.image_selected = pressed_image;
+ p.image_hover_selected = pressed_image;
+ // Use a glow effect when the user hovers over the button
+ // These icons are really small, need glow amount increased
+ p.hover_glow_amount( 0.33f );
p.click_callback.function(boost::bind(sButtonCallbacks[i], this));
p.tab_stop(false);
p.follows.flags(FOLLOWS_TOP|FOLLOWS_RIGHT);
- p.tool_tip(sButtonToolTips[i]);
- p.image_color(LLUI::getCachedColorFunctor("FloaterButtonImageColor"));
+ p.tool_tip = getButtonTooltip(floater_params, (EFloaterButton)i, getIsChrome());
p.scale_image(true);
+ p.chrome(true);
LLButton* buttonp = LLUICtrlFactory::create<LLButton>(p);
addChild(buttonp);
mButtons[i] = buttonp;
}
- updateButtons();
+ updateTitleButtons();
+}
+
+// static
+LLUIImage* LLFloater::getButtonImage(const Params& p, EFloaterButton e)
+{
+ switch(e)
+ {
+ default:
+ case BUTTON_CLOSE:
+ return p.close_image;
+ case BUTTON_RESTORE:
+ return p.restore_image;
+ case BUTTON_MINIMIZE:
+ return p.minimize_image;
+ case BUTTON_TEAR_OFF:
+ return p.tear_off_image;
+ case BUTTON_DOCK:
+ return p.dock_image;
+ case BUTTON_HELP:
+ return p.help_image;
+ }
+}
+
+// static
+LLUIImage* LLFloater::getButtonPressedImage(const Params& p, EFloaterButton e)
+{
+ switch(e)
+ {
+ default:
+ case BUTTON_CLOSE:
+ return p.close_pressed_image;
+ case BUTTON_RESTORE:
+ return p.restore_pressed_image;
+ case BUTTON_MINIMIZE:
+ return p.minimize_pressed_image;
+ case BUTTON_TEAR_OFF:
+ return p.tear_off_pressed_image;
+ case BUTTON_DOCK:
+ return p.dock_pressed_image;
+ case BUTTON_HELP:
+ return p.help_pressed_image;
+ }
+}
+
+// static
+std::string LLFloater::getButtonTooltip(const Params& p, EFloaterButton e, bool is_chrome)
+{
+ // EXT-4081 (Lag Meter: Ctrl+W does not close floater)
+ // If floater is chrome set 'Close' text for close button's tooltip
+ if(is_chrome && BUTTON_CLOSE == e)
+ {
+ static std::string close_tooltip_chrome = LLTrans::getString("BUTTON_CLOSE_CHROME");
+ return close_tooltip_chrome;
+ }
+ // TODO: per-floater localizable tooltips set in XML
+ return sButtonToolTips[e];
}
/////////////////////////////////////////////////////
// LLFloaterView
+static LLDefaultChildRegistry::Register<LLFloaterView> r("floater_view");
+
LLFloaterView::LLFloaterView (const Params& p)
: LLUICtrl (p),
+
mFocusCycleMode(FALSE),
- mSnapOffsetBottom(0)
- ,mSnapOffsetRight(0)
+ mMinimizePositionVOffset(0),
+ mSnapOffsetBottom(0),
+ mSnapOffsetRight(0)
{
}
@@ -1735,41 +2018,50 @@ void LLFloaterView::reshapeFloater(S32 width, S32 height, BOOL called_from_paren
// dependents use same follow flags as their "dependee"
continue;
}
- LLRect r = floaterp->getRect();
-
- // Compute absolute distance from each edge of screen
- S32 left_offset = llabs(r.mLeft - 0);
- S32 right_offset = llabs(old_width - r.mRight);
-
- S32 top_offset = llabs(old_height - r.mTop);
- S32 bottom_offset = llabs(r.mBottom - 0);
// Make if follow the edge it is closest to
U32 follow_flags = 0x0;
- if (left_offset < right_offset)
+ if (floaterp->isMinimized())
{
- follow_flags |= FOLLOWS_LEFT;
+ follow_flags |= (FOLLOWS_LEFT | FOLLOWS_TOP);
}
else
{
- follow_flags |= FOLLOWS_RIGHT;
- }
+ LLRect r = floaterp->getRect();
- // "No vertical adjustment" usually means that the bottom of the view
- // has been pushed up or down. Hence we want the floaters to follow
- // the top.
- if (!adjust_vertical)
- {
- follow_flags |= FOLLOWS_TOP;
- }
- else if (top_offset < bottom_offset)
- {
- follow_flags |= FOLLOWS_TOP;
- }
- else
- {
- follow_flags |= FOLLOWS_BOTTOM;
+ // Compute absolute distance from each edge of screen
+ S32 left_offset = llabs(r.mLeft - 0);
+ S32 right_offset = llabs(old_width - r.mRight);
+
+ S32 top_offset = llabs(old_height - r.mTop);
+ S32 bottom_offset = llabs(r.mBottom - 0);
+
+
+ if (left_offset < right_offset)
+ {
+ follow_flags |= FOLLOWS_LEFT;
+ }
+ else
+ {
+ follow_flags |= FOLLOWS_RIGHT;
+ }
+
+ // "No vertical adjustment" usually means that the bottom of the view
+ // has been pushed up or down. Hence we want the floaters to follow
+ // the top.
+ if (!adjust_vertical)
+ {
+ follow_flags |= FOLLOWS_TOP;
+ }
+ else if (top_offset < bottom_offset)
+ {
+ follow_flags |= FOLLOWS_TOP;
+ }
+ else
+ {
+ follow_flags |= FOLLOWS_BOTTOM;
+ }
}
floaterp->setFollows(follow_flags);
@@ -1808,8 +2100,8 @@ void LLFloaterView::restoreAll()
LLRect LLFloaterView::findNeighboringPosition( LLFloater* reference_floater, LLFloater* neighbor )
{
LLRect base_rect = reference_floater->getRect();
- S32 width = neighbor->getRect().getWidth();
- S32 height = neighbor->getRect().getHeight();
+ LLRect::tCoordType width = neighbor->getRect().getWidth();
+ LLRect::tCoordType height = neighbor->getRect().getHeight();
LLRect new_rect = neighbor->getRect();
LLRect expanded_base_rect = base_rect;
@@ -1822,16 +2114,16 @@ LLRect LLFloaterView::findNeighboringPosition( LLFloater* reference_floater, LLF
if (sibling &&
sibling != neighbor &&
sibling->getVisible() &&
- expanded_base_rect.rectInRect(&sibling->getRect()))
+ expanded_base_rect.overlaps(sibling->getRect()))
{
base_rect.unionWith(sibling->getRect());
}
}
- S32 left_margin = llmax(0, base_rect.mLeft);
- S32 right_margin = llmax(0, getRect().getWidth() - base_rect.mRight);
- S32 top_margin = llmax(0, getRect().getHeight() - base_rect.mTop);
- S32 bottom_margin = llmax(0, base_rect.mBottom);
+ LLRect::tCoordType left_margin = llmax(0, base_rect.mLeft);
+ LLRect::tCoordType right_margin = llmax(0, getRect().getWidth() - base_rect.mRight);
+ LLRect::tCoordType top_margin = llmax(0, getRect().getHeight() - base_rect.mTop);
+ LLRect::tCoordType bottom_margin = llmax(0, base_rect.mBottom);
// find position for floater in following order
// right->left->bottom->top
@@ -1950,6 +2242,11 @@ void LLFloaterView::bringToFront(LLFloater* child, BOOL give_focus)
if (give_focus && !gFocusMgr.childHasKeyboardFocus(child))
{
child->setFocus(TRUE);
+ // floater did not take focus, so relinquish focus to world
+ if (!child->hasFocus())
+ {
+ gFocusMgr.setKeyboardFocus(NULL);
+ }
}
}
@@ -2016,18 +2313,20 @@ void LLFloaterView::focusFrontFloater()
void LLFloaterView::getMinimizePosition(S32 *left, S32 *bottom)
{
- static LLUICachedControl<S32> floater_header_size ("UIFloaterHeaderSize", 0);
+ const LLFloater::Params& default_params = LLFloater::getDefaultParams();
+ S32 floater_header_size = default_params.header_height;
static LLUICachedControl<S32> minimized_width ("UIMinimizedWidth", 0);
- S32 col = 0;
LLRect snap_rect_local = getLocalSnapRect();
- for(S32 row = snap_rect_local.mBottom;
- row < snap_rect_local.getHeight() - floater_header_size;
- row += floater_header_size ) //loop rows
- {
- for(col = snap_rect_local.mLeft;
- col < snap_rect_local.getWidth() - minimized_width;
- col += minimized_width)
+ snap_rect_local.mTop += mMinimizePositionVOffset;
+ for(S32 col = snap_rect_local.mLeft;
+ col < snap_rect_local.getWidth() - minimized_width;
+ col += minimized_width)
+ {
+ for(S32 row = snap_rect_local.mTop - floater_header_size;
+ row > floater_header_size;
+ row -= floater_header_size ) //loop rows
{
+
bool foundGap = TRUE;
for(child_list_const_iter_t child_it = getChildList()->begin();
child_it != getChildList()->end();
@@ -2091,7 +2390,9 @@ void LLFloaterView::closeAllChildren(bool app_quitting)
// Attempt to close floater. This will cause the "do you want to save"
// dialogs to appear.
- if (floaterp->canClose() && !floaterp->isDead())
+ // Skip invisible floaters if we're not quitting (STORM-192).
+ if (floaterp->canClose() && !floaterp->isDead() &&
+ (app_quitting || floaterp->getVisible()))
{
floaterp->closeFloater(app_quitting);
}
@@ -2116,6 +2417,19 @@ BOOL LLFloaterView::allChildrenClosed()
return true;
}
+void LLFloaterView::shiftFloaters(S32 x_offset, S32 y_offset)
+{
+ for (child_list_const_iter_t it = getChildList()->begin(); it != getChildList()->end(); ++it)
+ {
+ LLFloater* floaterp = dynamic_cast<LLFloater*>(*it);
+
+ if (floaterp && floaterp->isMinimized())
+ {
+ floaterp->translate(x_offset, y_offset);
+ }
+ }
+}
+
void LLFloaterView::refresh()
{
// Constrain children to be entirely on the screen
@@ -2137,12 +2451,12 @@ void LLFloaterView::adjustToFitScreen(LLFloater* floater, BOOL allow_partial_out
// floater is hosted elsewhere, so ignore
return;
}
- S32 screen_width = getSnapRect().getWidth();
- S32 screen_height = getSnapRect().getHeight();
- // convert to local coordinate frame
- LLRect snap_rect_local = getLocalSnapRect();
+ LLRect::tCoordType screen_width = getSnapRect().getWidth();
+ LLRect::tCoordType screen_height = getSnapRect().getHeight();
- if( floater->isResizable() )
+
+ // only automatically resize non-minimized, resizable floaters
+ if( floater->isResizable() && !floater->isMinimized() )
{
LLRect view_rect = floater->getRect();
S32 old_width = view_rect.getWidth();
@@ -2165,7 +2479,11 @@ void LLFloaterView::adjustToFitScreen(LLFloater* floater, BOOL allow_partial_out
new_width = llmax(new_width, min_width);
new_height = llmax(new_height, min_height);
- floater->reshape( new_width, new_height, TRUE );
+ LLRect new_rect;
+ new_rect.setLeftTopAndSize(view_rect.mLeft,view_rect.mTop,new_width, new_height);
+
+ floater->setShape(new_rect);
+
if (floater->followsRight())
{
floater->translate(old_width - new_width, 0);
@@ -2179,7 +2497,7 @@ void LLFloaterView::adjustToFitScreen(LLFloater* floater, BOOL allow_partial_out
}
// move window fully onscreen
- if (floater->translateIntoRect( snap_rect_local, allow_partial_outside ))
+ if (floater->translateIntoRect( getLocalRect(), allow_partial_outside ))
{
floater->clearSnapTarget();
}
@@ -2262,7 +2580,7 @@ LLFloater *LLFloaterView::getBackmost() const
void LLFloaterView::syncFloaterTabOrder()
{
- // look for a visible modal dialog, starting from first (should be only one)
+ // look for a visible modal dialog, starting from first
LLModalDialog* modal_dialog = NULL;
for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
{
@@ -2277,10 +2595,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 ) )
{
@@ -2358,12 +2673,18 @@ 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)
{
- for (child_list_const_iter_t child_iter = getChildList()->begin();
- child_iter != getChildList()->end(); ++child_iter)
+ // make a copy of the list since some floaters change their
+ // order in the childList when changing visibility.
+ child_list_t child_list_copy = *getChildList();
+
+ for (child_list_const_iter_t child_iter = child_list_copy.begin();
+ child_iter != child_list_copy.end(); ++child_iter)
{
LLView *view = *child_iter;
if (skip_list.find(view) == skip_list.end())
@@ -2371,6 +2692,8 @@ void LLFloaterView::popVisibleAll(const skip_list_t& skip_list)
view->popVisible();
}
}
+
+ LLFloaterReg::blockShowFloaters(false);
}
void LLFloater::setInstanceName(const std::string& name)
@@ -2381,15 +2704,22 @@ void LLFloater::setInstanceName(const std::string& name)
mInstanceName = name;
if (!mInstanceName.empty())
{
+ std::string ctrl_name = getControlName(mInstanceName, mKey);
+
// save_rect and save_visibility only apply to registered floaters
if (!mRectControl.empty())
{
- mRectControl = LLFloaterReg::declareRectControl(mInstanceName);
+ mRectControl = LLFloaterReg::declareRectControl(ctrl_name);
}
if (!mVisibilityControl.empty())
{
- mVisibilityControl = LLFloaterReg::declareVisibilityControl(mInstanceName);
+ mVisibilityControl = LLFloaterReg::declareVisibilityControl(ctrl_name);
}
+ if(!mDocStateControl.empty())
+ {
+ mDocStateControl = LLFloaterReg::declareDockStateControl(ctrl_name);
+ }
+
}
}
@@ -2430,6 +2760,9 @@ void LLFloater::setupParamsForExport(Params& p, LLView* parent)
void LLFloater::initFromParams(const LLFloater::Params& p)
{
+ // *NOTE: We have too many classes derived from LLFloater to retrofit them
+ // all to pass in params via constructors. So we use this method.
+
// control_name, tab_stop, focus_lost_callback, initial_value, rect, enabled, visible
LLPanel::initFromParams(p);
@@ -2440,13 +2773,16 @@ void LLFloater::initFromParams(const LLFloater::Params& p)
setCanTearOff(p.can_tear_off);
setCanMinimize(p.can_minimize);
setCanClose(p.can_close);
+ setCanDock(p.can_dock);
+ setCanResize(p.can_resize);
+ setResizeLimits(p.min_width, p.min_height);
mDragOnLeft = p.can_drag_on_left;
- mResizable = p.can_resize;
- mMinWidth = p.min_width;
- mMinHeight = p.min_height;
+ mHeaderHeight = p.header_height;
+ mLegacyHeaderHeight = p.legacy_header_height;
mSingleInstance = p.single_instance;
mAutoTile = p.auto_tile;
+ mOpenCentered = p.open_centered;
if (p.save_rect)
{
@@ -2456,27 +2792,58 @@ void LLFloater::initFromParams(const LLFloater::Params& p)
{
mVisibilityControl = "t"; // flag to build mVisibilityControl name once mInstanceName is set
}
+
+ if(p.save_dock_state)
+ {
+ mDocStateControl = "t"; // flag to build mDocStateControl name once mInstanceName is set
+ }
+
+ // open callback
+ if (p.open_callback.isProvided())
+ {
+ mOpenSignal.connect(initCommitCallback(p.open_callback));
+ }
+ // close callback
+ if (p.close_callback.isProvided())
+ {
+ mCloseSignal.connect(initCommitCallback(p.close_callback));
+ }
}
-void LLFloater::initFloaterXML(LLXMLNodePtr node, LLView *parent, BOOL open_floater, LLXMLNodePtr output_node)
+LLFastTimer::DeclareTimer POST_BUILD("Floater Post Build");
+
+bool LLFloater::initFloaterXML(LLXMLNodePtr node, LLView *parent, const std::string& filename, LLXMLNodePtr output_node)
{
- Params params(LLUICtrlFactory::getDefaultParams<LLFloater::Params>());
- LLXUIParser::instance().readXUI(node, params);
+ Params params(LLUICtrlFactory::getDefaultParams<LLFloater>());
+ LLXUIParser::instance().readXUI(node, params, filename); // *TODO: Error checking
if (output_node)
{
Params output_params(params);
setupParamsForExport(output_params, parent);
- Params default_params(LLUICtrlFactory::getDefaultParams<LLFloater::Params>());
+ Params default_params(LLUICtrlFactory::getDefaultParams<LLFloater>());
output_node->setName(node->getName()->mString);
LLXUIParser::instance().writeXUI(
output_node, output_params, &default_params);
}
- setupParams(params, parent);
+ // Default floater position to top-left corner of screen
+ // However, some legacy floaters have explicit top or bottom
+ // coordinates set, so respect their wishes.
+ if (!params.rect.top.isProvided() && !params.rect.bottom.isProvided())
+ {
+ params.rect.top.set(0);
+ }
+ if (!params.rect.left.isProvided() && !params.rect.right.isProvided())
+ {
+ params.rect.left.set(0);
+ }
+
+ params.from_xui = true;
+ applyXUILayout(params, parent);
initFromParams(params);
- initFloater();
+ initFloater(params);
LLMultiFloater* last_host = LLFloater::getFloaterHost();
if (node->hasName("multi_floater"))
@@ -2484,14 +2851,36 @@ void LLFloater::initFloaterXML(LLXMLNodePtr node, LLView *parent, BOOL open_floa
LLFloater::setFloaterHost((LLMultiFloater*) this);
}
- LLUICtrlFactory::createChildren(this, node, output_node);
+ LLUICtrlFactory::createChildren(this, node, child_registry_t::instance(), output_node);
if (node->hasName("multi_floater"))
{
LLFloater::setFloaterHost(last_host);
}
- BOOL result = postBuild();
+ // HACK: When we changed the header height to 25 pixels in Viewer 2, rather
+ // than re-layout all the floaters we use this value in pixels to make the
+ // whole floater bigger and change the top-left coordinate for widgets.
+ // The goal is to eventually set mLegacyHeaderHeight to zero, which would
+ // make the top-left corner for widget layout the same as the top-left
+ // corner of the window's content area. James
+ S32 header_stretch = (mHeaderHeight - mLegacyHeaderHeight);
+ if (header_stretch > 0)
+ {
+ // Stretch the floater vertically, don't move widgets
+ LLRect rect = getRect();
+ rect.mTop += header_stretch;
+
+ // This will also update drag handle, title bar, close box, etc.
+ setRect(rect);
+ }
+
+ BOOL result;
+ {
+ LLFastTimer ft(POST_BUILD);
+
+ result = postBuild();
+ }
if (!result)
{
@@ -2501,38 +2890,32 @@ void LLFloater::initFloaterXML(LLXMLNodePtr node, LLView *parent, BOOL open_floa
applyRectControl(); // If we have a saved rect control, apply it
gFloaterView->adjustToFitScreen(this, FALSE); // Floaters loaded from XML should all fit on screen
- if (open_floater)
- {
- this->openFloater(getKey());
- }
-
moveResizeHandlesToFront();
+
+ applyDockState();
+
+ return true; // *TODO: Error checking
}
-// visibility methods
-bool VisibilityPolicy<LLFloater>::visible(LLFloater* instance, const LLSD& key)
+bool LLFloater::isShown() const
{
- if (instance)
- {
- return !instance->isMinimized() && instance->isInVisibleChain();
- }
- return FALSE;
+ return ! isMinimized() && isInVisibleChain();
}
-void VisibilityPolicy<LLFloater>::show(LLFloater* instance, const LLSD& key)
+/* static */
+bool LLFloater::isShown(const LLFloater* floater)
{
- if (instance)
- {
- instance->openFloater(key);
- if (instance->getHost())
- {
- instance->getHost()->openFloater(key);
- }
- }
+ return floater && floater->isShown();
}
-void VisibilityPolicy<LLFloater>::hide(LLFloater* instance, const LLSD& key)
+/* static */
+bool LLFloater::isMinimized(const LLFloater* floater)
{
- if (instance) instance->closeFloater();
+ 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 421b7f3ec1..5ecf515cf9 100644
--- a/indra/llui/llfloater.h
+++ b/indra/llui/llfloater.h
@@ -2,31 +2,25 @@
* @file llfloater.h
* @brief LLFloater base class
*
- * $LicenseInfo:firstyear=2002&license=viewergpl$
- *
- * Copyright (c) 2002-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -39,7 +33,7 @@
#include "llpanel.h"
#include "lluuid.h"
-#include "llnotifications.h"
+//#include "llnotificationsutil.h"
#include <set>
class LLDragHandle;
@@ -65,20 +59,6 @@ const BOOL CLOSE_NO = FALSE;
const BOOL ADJUST_VERTICAL_YES = TRUE;
const BOOL ADJUST_VERTICAL_NO = FALSE;
-// associates a given notification instance with a particular floater
-class LLFloaterNotificationContext :
- public LLNotificationContext
-{
-public:
- LLFloaterNotificationContext(LLHandle<LLFloater> floater_handle) :
- mFloaterHandle(floater_handle)
- {}
-
- LLFloater* getFloater() { return mFloaterHandle.get(); }
-private:
- LLHandle<LLFloater> mFloaterHandle;
-};
-
class LLFloater : public LLPanel
{
friend class LLFloaterView;
@@ -87,21 +67,24 @@ friend class LLMultiFloater;
public:
struct KeyCompare
{
- static bool compare(const LLSD& a, const LLSD& b);
+// static bool compare(const LLSD& a, const LLSD& b);
static bool equate(const LLSD& a, const LLSD& b);
+/*==========================================================================*|
bool operator()(const LLSD& a, const LLSD& b) const
{
return compare(a, b);
}
+|*==========================================================================*/
};
- enum EFloaterButtons
+ enum EFloaterButton
{
- BUTTON_CLOSE,
+ BUTTON_CLOSE = 0,
BUTTON_RESTORE,
BUTTON_MINIMIZE,
BUTTON_TEAR_OFF,
- BUTTON_EDIT,
+ BUTTON_DOCK,
+ BUTTON_HELP,
BUTTON_COUNT
};
@@ -119,29 +102,40 @@ public:
can_drag_on_left,
can_tear_off,
save_rect,
- save_visibility;
-
- Params() :
- title("title"),
- short_title("short_title"),
- single_instance("single_instance", false),
- auto_tile("auto_tile", false),
- can_resize("can_resize", false),
- can_minimize("can_minimize", true),
- can_close("can_close", true),
- can_drag_on_left("can_drag_on_left", false),
- can_tear_off("can_tear_off", true),
- save_rect("save_rect", false),
- save_visibility("save_visibility", false)
- {
- name = "floater";
- // defaults that differ from LLPanel:
- background_visible = true;
- visible = false;
- }
+ save_visibility,
+ save_dock_state,
+ can_dock,
+ open_centered;
+ Optional<S32> header_height,
+ legacy_header_height; // HACK see initFromXML()
+
+ // Images for top-right controls
+ Optional<LLUIImage*> close_image,
+ restore_image,
+ minimize_image,
+ tear_off_image,
+ dock_image,
+ help_image;
+ Optional<LLUIImage*> close_pressed_image,
+ restore_pressed_image,
+ minimize_pressed_image,
+ tear_off_pressed_image,
+ dock_pressed_image,
+ help_pressed_image;
+
+ Optional<CommitCallbackParam> open_callback,
+ close_callback;
+
+ Params();
};
- LLFloater(const LLSD& key = LLSD(), const LLFloater::Params& params = LLFloater::Params());
+ // use this to avoid creating your own default LLFloater::Param instance
+ static const Params& getDefaultParams();
+
+ // Load translations for tooltips for standard buttons
+ static void initClass();
+
+ LLFloater(const LLSD& key, const Params& params = getDefaultParams());
virtual ~LLFloater();
@@ -149,7 +143,7 @@ public:
static void setupParamsForExport(Params& p, LLView* parent);
void initFromParams(const LLFloater::Params& p);
- void initFloaterXML(LLXMLNodePtr node, LLView *parent, BOOL open_floater = TRUE, LLXMLNodePtr output_node = NULL);
+ bool initFloaterXML(LLXMLNodePtr node, LLView *parent, const std::string& filename, LLXMLNodePtr output_node = NULL);
/*virtual*/ void handleReshape(const LLRect& new_rect, bool by_user = false);
/*virtual*/ BOOL canSnapTo(const LLView* other_view);
@@ -158,12 +152,11 @@ public:
/*virtual*/ void setIsChrome(BOOL is_chrome);
/*virtual*/ void setRect(const LLRect &rect);
- void initFloater();
+ void initFloater(const Params& p);
void openFloater(const LLSD& key = LLSD());
// If allowed, close the floater cleanly, releasing focus.
- // app_quitting is passed to onClose() below.
void closeFloater(bool app_quitting = false);
/*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
@@ -177,11 +170,11 @@ public:
LLMultiFloater* getHost();
void applyTitle();
- const std::string& getCurrentTitle() const;
+ std::string getCurrentTitle() const;
void setTitle( const std::string& title);
- std::string getTitle();
+ std::string getTitle() const;
void setShortTitle( const std::string& short_title );
- std::string getShortTitle();
+ std::string getShortTitle() const;
void setTitleVisible(bool visible);
virtual void setMinimized(BOOL b);
void moveResizeHandlesToFront();
@@ -189,7 +182,16 @@ public:
void addDependentFloater(LLHandle<LLFloater> dependent_handle, BOOL reposition = TRUE);
LLFloater* getDependee() { return (LLFloater*)mDependeeHandle.get(); }
void removeDependentFloater(LLFloater* dependent);
- BOOL isMinimized() { return mMinimized; }
+ BOOL isMinimized() const { return mMinimized; }
+ /// isShown() differs from getVisible() in that isShown() also considers
+ /// isMinimized(). isShown() is true only if visible and not minimized.
+ bool isShown() const;
+ /// 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(); }
void setCanMinimize(BOOL can_minimize);
@@ -201,32 +203,38 @@ public:
BOOL isResizable() const { return mResizable; }
void setResizeLimits( S32 min_width, S32 min_height );
void getResizeLimits( S32* min_width, S32* min_height ) { *min_width = mMinWidth; *min_height = mMinHeight; }
+ LLRect getSavedRect() const;
+ bool hasSavedRect() const;
+
+ static std::string getControlName(const std::string& name, const LLSD& key);
+ static LLControlGroup* getControlGroup();
bool isMinimizeable() const{ return mCanMinimize; }
bool isCloseable() const{ return mCanClose; }
bool isDragOnLeft() const{ return mDragOnLeft; }
S32 getMinWidth() const{ return mMinWidth; }
S32 getMinHeight() const{ return mMinHeight; }
+ S32 getHeaderHeight() const { return mHeaderHeight; }
virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask);
virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask);
virtual BOOL handleMiddleMouseDown(S32 x, S32 y, MASK mask);
+
+ virtual BOOL handleScrollWheel(S32 x, S32 y, S32 mask);
+
virtual void draw();
-
+ virtual void drawShadow(LLPanel* panel);
+
virtual void onOpen(const LLSD& key) {}
-
- // Call destroy() to free memory, or setVisible(FALSE) to keep it
- // If app_quitting, you might not want to save your visibility.
- // Defaults to destroy().
- virtual void onClose(bool app_quitting) { destroy(); }
+ virtual void onClose(bool app_quitting) {}
// This cannot be "const" until all derived floater canClose()
// methods are const as well. JC
virtual BOOL canClose() { return TRUE; }
- virtual void setVisible(BOOL visible);
- virtual void onVisibilityChange ( BOOL curVisibilityIn );
+ /*virtual*/ void setVisible(BOOL visible); // do not override
+ /*virtual*/ void handleVisibilityChange ( BOOL new_visibility ); // do not override
void setFrontmost(BOOL take_focus = TRUE);
@@ -242,6 +250,16 @@ public:
LLHandle<LLFloater> getHandle() const { return mHandle; }
const LLSD& getKey() { return mKey; }
BOOL matchesKey(const LLSD& key) { return mSingleInstance || KeyCompare::equate(key, mKey); }
+
+ const std::string& getInstanceName() { return mInstanceName; }
+
+ bool isDockable() const { return mCanDock; }
+ void setCanDock(bool b);
+
+ bool isDocked() const { return mDocked; }
+ virtual void setDocked(bool docked, bool pop_on_undock = true);
+
+ virtual void setTornOff(bool torn_off) { mTornOff = torn_off; }
// Return a closeable floater, if any, given the current focus.
static LLFloater* getClosableFloaterFromFocus();
@@ -250,27 +268,31 @@ public:
// handle refocusing.
static void closeFocusedFloater();
- LLNotification::Params contextualNotification(const std::string& name)
- {
- return LLNotification::Params(name).context(mNotificationContext);
- }
+// LLNotification::Params contextualNotification(const std::string& name)
+// {
+// return LLNotification::Params(name).context(mNotificationContext);
+// }
static void onClickClose(LLFloater* floater);
static void onClickMinimize(LLFloater* floater);
static void onClickTearOff(LLFloater* floater);
- static void onClickEdit(LLFloater* floater);
+ static void onClickDock(LLFloater* floater);
+ static void onClickHelp(LLFloater* floater);
static void setFloaterHost(LLMultiFloater* hostp) {sHostp = hostp; }
- static void setEditModeEnabled(BOOL enable);
- static BOOL getEditModeEnabled() { return sEditModeEnabled; }
static LLMultiFloater* getFloaterHost() {return sHostp; }
protected:
void setRectControl(const std::string& rectname) { mRectControl = rectname; };
+
+ virtual void applySavedVariables();
+
void applyRectControl();
+ void applyDockState();
void storeRectControl();
void storeVisibilityControl();
+ void storeDockStateControl();
void setKey(const LLSD& key);
void setInstanceName(const std::string& name);
@@ -282,32 +304,62 @@ 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 close(). JC
+ void destroy(); // 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();
- BOOL offerClickToButton(S32 x, S32 y, MASK mask, EFloaterButtons index);
+ void buildButtons(const Params& p);
+
+ // Images and tooltips are named in the XML, but we want to look them
+ // up by index.
+ static LLUIImage* getButtonImage(const Params& p, EFloaterButton e);
+ static LLUIImage* getButtonPressedImage(const Params& p, EFloaterButton e);
+
+ /**
+ * @params is_chrome - if floater is Chrome it means that floater will never get focus.
+ * Therefore it can't be closed with 'Ctrl+W'. So the tooltip text of close button( X )
+ * should be 'Close' not 'Close(Ctrl+W)' as for usual floaters.
+ */
+ static std::string getButtonTooltip(const Params& p, EFloaterButton e, bool is_chrome);
+
+ BOOL offerClickToButton(S32 x, S32 y, MASK mask, EFloaterButton index);
void addResizeCtrls();
+ void layoutResizeCtrls();
+ void enableResizeCtrls(bool enable);
void addDragHandle();
-
+ void layoutDragHandle(); // repair layout
+
+public:
+ // Called when floater is opened, passes mKey
+ // Public so external views or floaters can watch for this floater opening
+ commit_signal_t mOpenSignal;
+
+ // Called when floater is closed, passes app_qitting as LLSD()
+ // Public so external views or floaters can watch for this floater closing
+ commit_signal_t mCloseSignal;
+
protected:
std::string mRectControl;
std::string mVisibilityControl;
-
+ std::string mDocStateControl;
LLSD mKey; // Key used for retrieving instances; set (for now) by LLFLoaterReg
-
-private:
- LLRect mExpandedRect;
+
LLDragHandle* mDragHandle;
LLResizeBar* mResizeBar[4];
LLResizeHandle* mResizeHandle[4];
+
+ LLButton* mButtons[BUTTON_COUNT];
+private:
+ LLRect mExpandedRect;
LLUIString mTitle;
LLUIString mShortTitle;
@@ -321,9 +373,12 @@ private:
BOOL mCanClose;
BOOL mDragOnLeft;
BOOL mResizable;
+ bool mOpenCentered;
S32 mMinWidth;
S32 mMinHeight;
+ S32 mHeaderHeight; // height in pixels of header for title, drag bar
+ S32 mLegacyHeaderHeight;// HACK see initFloaterXML()
BOOL mMinimized;
BOOL mForeground;
@@ -331,14 +386,12 @@ private:
BOOL mFirstLook; // TRUE if the _next_ time this floater is visible will be the first time in the session that it is visible.
- BOOL mEditing;
typedef std::set<LLHandle<LLFloater> > handle_set_t;
typedef std::set<LLHandle<LLFloater> >::iterator handle_set_iter_t;
handle_set_t mDependents;
- BOOL mButtonsEnabled[BUTTON_COUNT];
- LLButton* mButtons[BUTTON_COUNT];
+ bool mButtonsEnabled[BUTTON_COUNT];
F32 mButtonScale;
BOOL mAutoFocus;
LLHandle<LLFloater> mSnappedTo;
@@ -346,11 +399,12 @@ private:
LLHandle<LLFloater> mHostHandle;
LLHandle<LLFloater> mLastHostHandle;
+ bool mCanDock;
+ bool mDocked;
+ bool mTornOff;
+
static LLMultiFloater* sHostp;
- static BOOL sEditModeEnabled;
- static std::string sButtonActiveImageNames[BUTTON_COUNT];
- static std::string sButtonInactiveImageNames[BUTTON_COUNT];
- static std::string sButtonPressedImageNames[BUTTON_COUNT];
+ static BOOL sQuitting;
static std::string sButtonNames[BUTTON_COUNT];
static std::string sButtonToolTips[BUTTON_COUNT];
static std::string sButtonToolTipsIndex[BUTTON_COUNT];
@@ -368,24 +422,26 @@ private:
S32 mPreviousMinimizedBottom;
S32 mPreviousMinimizedLeft;
- LLColor4 mBgColorAlpha;
- LLColor4 mBgColorOpaque;
-
- LLFloaterNotificationContext* mNotificationContext;
+// LLFloaterNotificationContext* mNotificationContext;
LLRootHandle<LLFloater> mHandle;
};
+
/////////////////////////////////////////////////////////////
// LLFloaterView
// Parent of all floating panels
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);
@@ -398,6 +454,7 @@ public:
// Given a child of gFloaterView, make sure this view can fit entirely onscreen.
void adjustToFitScreen(LLFloater* floater, BOOL allow_partial_outside);
+ void setMinimizePositionVerticalOffset(S32 offset) { mMinimizePositionVOffset = offset; }
void getMinimizePosition( S32 *left, S32 *bottom);
void restoreAll(); // un-minimize all floaters
typedef std::set<LLView*> skip_list_t;
@@ -414,6 +471,7 @@ public:
// attempt to close all floaters
void closeAllChildren(bool app_quitting);
BOOL allChildrenClosed();
+ void shiftFloaters(S32 x_offset, S32 y_offset);
LLFloater* getFrontmost() const;
LLFloater* getBackmost() const;
@@ -430,39 +488,10 @@ public:
void setSnapOffsetRight(S32 offset) { mSnapOffsetRight = offset; }
private:
- S32 mColumn;
- S32 mNextLeft;
- S32 mNextTop;
BOOL mFocusCycleMode;
S32 mSnapOffsetBottom;
S32 mSnapOffsetRight;
-};
-
-// singleton implementation for floaters
-// https://wiki.lindenlab.com/mediawiki/index.php?title=LLFloaterSingleton&oldid=164990
-
-//*******************************************************
-//* TO BE DEPRECATED
-//*******************************************************
-// visibility policy specialized for floaters
-template<>
-class VisibilityPolicy<LLFloater>
-{
-public:
- // visibility methods
- static bool visible(LLFloater* instance, const LLSD& key);
-
- static void show(LLFloater* instance, const LLSD& key);
-
- static void hide(LLFloater* instance, const LLSD& key);
-};
-
-
-// singleton implementation for floaters (provides visibility policy)
-// https://wiki.lindenlab.com/mediawiki/index.php?title=LLFloaterSingleton&oldid=164990
-
-template <class T> class LLFloaterSingleton : public LLUISingleton<T, VisibilityPolicy<LLFloater> >
-{
+ S32 mMinimizePositionVOffset;
};
//
diff --git a/indra/llui/llfloaterreg.cpp b/indra/llui/llfloaterreg.cpp
index faa763cea9..4720ebb822 100644
--- a/indra/llui/llfloaterreg.cpp
+++ b/indra/llui/llfloaterreg.cpp
@@ -2,31 +2,25 @@
* @file llfloaterreg.cpp
* @brief LLFloaterReg Floater Registration Class
*
- * $LicenseInfo:firstyear=2002&license=viewergpl$
- *
- * Copyright (c) 2002-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -34,8 +28,10 @@
#include "llfloaterreg.h"
+//#include "llagent.h"
#include "llfloater.h"
#include "llmultifloater.h"
+#include "llfloaterreglistener.h"
//*******************************************************
@@ -44,6 +40,10 @@ 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;
+std::set<std::string> LLFloaterReg::sAlwaysShowableList;
+
+static LLFloaterRegListener sFloaterRegListener;
//*******************************************************
@@ -121,13 +121,17 @@ LLFloater* LLFloaterReg::getInstance(const std::string& name, const LLSD& key)
res = build_func(key);
- const bool DONT_OPEN_FLOATER = false;
- LLUICtrlFactory::getInstance()->buildFloater(res, xui_file, DONT_OPEN_FLOATER);
-
+ bool success = LLUICtrlFactory::getInstance()->buildFloater(res, xui_file, NULL);
+ if (!success)
+ {
+ llwarns << "Failed to build floater type: '" << name << "'." << llendl;
+ return NULL;
+ }
+
// Note: key should eventually be a non optional LLFloater arg; for now, set mKey to be safe
res->mKey = key;
res->setInstanceName(name);
- res->applyRectControl(); // Can't apply rect control until setting instance name
+ res->applySavedVariables(); // Can't apply rect and dock state until setting instance name
if (res->mAutoTile && !res->getHost() && index > 0)
{
const LLRect& cur_rect = res->getRect();
@@ -210,6 +214,10 @@ 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
+ // see EXT-7090
+ && sAlwaysShowableList.find(name) == sAlwaysShowableList.end())
+ return 0;//
LLFloater* instance = getInstance(name, key);
if (instance)
{
@@ -245,7 +253,7 @@ bool LLFloaterReg::hideInstance(const std::string& name, const LLSD& key)
bool LLFloaterReg::toggleInstance(const std::string& name, const LLSD& key)
{
LLFloater* instance = findInstance(name, key);
- if (instance && !instance->isMinimized() && instance->isInVisibleChain())
+ if (LLFloater::isShown(instance))
{
// When toggling *visibility*, close the host instead of the floater when hosted
if (instance->getHost())
@@ -261,18 +269,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);
- if (instance && !instance->isMinimized() && instance->isInVisibleChain())
- {
- return true;
- }
- else
- {
- return false;
- }
+ return LLFloater::isVisible(instance);
}
//static
@@ -283,9 +284,9 @@ void LLFloaterReg::showInitialVisibleInstances()
{
const std::string& name = iter->first;
std::string controlname = getVisibilityControlName(name);
- if (LLUI::sSettingGroups["floater"]->controlExists(controlname))
+ if (LLFloater::getControlGroup()->controlExists(controlname))
{
- BOOL isvis = LLUI::sSettingGroups["floater"]->getBOOL(controlname);
+ BOOL isvis = LLFloater::getControlGroup()->getBOOL(controlname);
if (isvis)
{
showInstance(name, LLSD()); // keyed floaters shouldn't set save_vis to true
@@ -339,7 +340,7 @@ std::string LLFloaterReg::getRectControlName(const std::string& name)
std::string LLFloaterReg::declareRectControl(const std::string& name)
{
std::string controlname = getRectControlName(name);
- LLUI::sSettingGroups["floater"]->declareRect(controlname, LLRect(),
+ LLFloater::getControlGroup()->declareRect(controlname, LLRect(),
llformat("Window Position and Size for %s", name.c_str()),
TRUE);
return controlname;
@@ -357,12 +358,58 @@ std::string LLFloaterReg::getVisibilityControlName(const std::string& name)
std::string LLFloaterReg::declareVisibilityControl(const std::string& name)
{
std::string controlname = getVisibilityControlName(name);
- LLUI::sSettingGroups["floater"]->declareBOOL(controlname, FALSE,
+ LLFloater::getControlGroup()->declareBOOL(controlname, FALSE,
llformat("Window Visibility for %s", name.c_str()),
TRUE);
return controlname;
}
+//static
+std::string LLFloaterReg::declareDockStateControl(const std::string& name)
+{
+ std::string controlname = getDockStateControlName(name);
+ LLFloater::getControlGroup()->declareBOOL(controlname, TRUE,
+ llformat("Window Docking state for %s", name.c_str()),
+ TRUE);
+ return controlname;
+
+}
+
+//static
+std::string LLFloaterReg::getDockStateControlName(const std::string& name)
+{
+ std::string res = std::string("floater_dock_") + name;
+ LLStringUtil::replaceChar( res, ' ', '_' );
+ return res;
+}
+
+
+//static
+void LLFloaterReg::registerControlVariables()
+{
+ // Iterate through alll registered instance names and register rect and visibility control variables
+ for (build_map_t::iterator iter = sBuildMap.begin(); iter != sBuildMap.end(); ++iter)
+ {
+ const std::string& name = iter->first;
+ if (LLFloater::getControlGroup()->controlExists(getRectControlName(name)))
+ {
+ declareRectControl(name);
+ }
+ if (LLFloater::getControlGroup()->controlExists(getVisibilityControlName(name)))
+ {
+ declareVisibilityControl(name);
+ }
+ }
+
+ const LLSD& exclude_list = LLUI::sSettingGroups["config"]->getLLSD("always_showable_floaters");
+ for (LLSD::array_const_iterator iter = exclude_list.beginArray();
+ iter != exclude_list.endArray();
+ iter++)
+ {
+ sAlwaysShowableList.insert(iter->asString());
+ }
+}
+
// Callbacks
// static
@@ -372,7 +419,7 @@ void LLFloaterReg::initUICtrlToFloaterVisibilityControl(LLUICtrl* ctrl, const LL
// Get the visibility control name for the floater
std::string vis_control_name = LLFloaterReg::declareVisibilityControl(sdname.asString());
// Set the control value to the floater visibility control (Sets the value as well)
- ctrl->setControlVariable(LLUI::sSettingGroups["floater"]->getControl(vis_control_name));
+ ctrl->setControlVariable(LLFloater::getControlGroup()->getControl(vis_control_name));
}
// callback args may use "floatername.key" format
@@ -421,3 +468,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 ef2f71ad18..94387fb41a 100644
--- a/indra/llui/llfloaterreg.h
+++ b/indra/llui/llfloaterreg.h
@@ -2,31 +2,25 @@
* @file llfloaterreg.h
* @brief LLFloaterReg Floater Registration Class
*
- * $LicenseInfo:firstyear=2002&license=viewergpl$
- *
- * Copyright (c) 2002-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LLFLOATERREG_H
@@ -70,10 +64,16 @@ public:
typedef std::map<std::string, BuildData> build_map_t;
private:
+ friend class LLFloaterRegListener;
static instance_list_t sNullInstanceList;
static instance_map_t sInstanceMap;
static build_map_t sBuildMap;
static std::map<std::string,std::string> sGroupMap;
+ static bool sBlockShowFloaters;
+ /**
+ * Defines list of floater names that can be shown despite state of sBlockShowFloaters.
+ */
+ static std::set<std::string> sAlwaysShowableList;
public:
// Registration
@@ -121,12 +121,18 @@ public:
static std::string getVisibilityControlName(const std::string& name);
static std::string declareVisibilityControl(const std::string& name);
+ static std::string declareDockStateControl(const std::string& name);
+ static std::string getDockStateControlName(const std::string& name);
+
+ static void registerControlVariables();
+
// Callback wrappers
static void initUICtrlToFloaterVisibilityControl(LLUICtrl* ctrl, const LLSD& sdname);
static void showFloaterInstance(const LLSD& sdname);
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>
@@ -146,6 +152,8 @@ public:
{
return dynamic_cast<T*>(showInstance(name, key, focus));
}
+
+ static void blockShowFloaters(bool value) { sBlockShowFloaters = value;}
};
diff --git a/indra/llui/llfloaterreglistener.cpp b/indra/llui/llfloaterreglistener.cpp
new file mode 100644
index 0000000000..821d4543ae
--- /dev/null
+++ b/indra/llui/llfloaterreglistener.cpp
@@ -0,0 +1,147 @@
+/**
+ * @file llfloaterreglistener.cpp
+ * @author Nat Goodspeed
+ * @date 2009-08-12
+ * @brief Implementation for llfloaterreglistener.
+ *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+// Precompiled header
+#include "linden_common.h"
+// associated header
+#include "llfloaterreglistener.h"
+// STL headers
+// std headers
+// external library headers
+// other Linden headers
+#include "llfloaterreg.h"
+#include "llfloater.h"
+#include "llbutton.h"
+
+LLFloaterRegListener::LLFloaterRegListener():
+ LLEventAPI("LLFloaterReg",
+ "LLFloaterReg listener to (e.g.) show/hide LLFloater instances")
+{
+ add("getBuildMap",
+ "Return on [\"reply\"] data about all registered LLFloaterReg floater names",
+ &LLFloaterRegListener::getBuildMap,
+ LLSD().with("reply", LLSD()));
+ LLSD requiredName;
+ requiredName["name"] = LLSD();
+ add("showInstance",
+ "Ask to display the floater specified in [\"name\"]",
+ &LLFloaterRegListener::showInstance,
+ requiredName);
+ add("hideInstance",
+ "Ask to hide the floater specified in [\"name\"]",
+ &LLFloaterRegListener::hideInstance,
+ requiredName);
+ add("toggleInstance",
+ "Ask to toggle the state of the floater specified in [\"name\"]",
+ &LLFloaterRegListener::toggleInstance,
+ requiredName);
+ LLSD requiredNameButton;
+ requiredNameButton["name"] = LLSD();
+ requiredNameButton["button"] = LLSD();
+ add("clickButton",
+ "Simulate clicking the named [\"button\"] in the visible floater named in [\"name\"]",
+ &LLFloaterRegListener::clickButton,
+ requiredNameButton);
+}
+
+void LLFloaterRegListener::getBuildMap(const LLSD& event) const
+{
+ // Honor the "reqid" convention by echoing event["reqid"] in our reply packet.
+ LLReqID reqID(event);
+ LLSD reply(reqID.makeResponse());
+ // Build an LLSD map that mirrors sBuildMap. Since we have no good way to
+ // represent a C++ callable in LLSD, the only part of BuildData we can
+ // store is the filename. For each LLSD map entry, it would be more
+ // extensible to store a nested LLSD map containing a single key "file" --
+ // but we don't bother, simply storing the string filename instead.
+ for (LLFloaterReg::build_map_t::const_iterator mi(LLFloaterReg::sBuildMap.begin()),
+ mend(LLFloaterReg::sBuildMap.end());
+ mi != mend; ++mi)
+ {
+ reply[mi->first] = mi->second.mFile;
+ }
+ // Send the reply to the LLEventPump named in event["reply"].
+ LLEventPumps::instance().obtain(event["reply"]).post(reply);
+}
+
+void LLFloaterRegListener::showInstance(const LLSD& event) const
+{
+ LLFloaterReg::showInstance(event["name"], event["key"], event["focus"]);
+}
+
+void LLFloaterRegListener::hideInstance(const LLSD& event) const
+{
+ LLFloaterReg::hideInstance(event["name"], event["key"]);
+}
+
+void LLFloaterRegListener::toggleInstance(const LLSD& event) const
+{
+ LLFloaterReg::toggleInstance(event["name"], event["key"]);
+}
+
+void LLFloaterRegListener::clickButton(const LLSD& event) const
+{
+ // If the caller requests a reply, build the reply.
+ LLReqID reqID(event);
+ LLSD reply(reqID.makeResponse());
+
+ LLFloater* floater = LLFloaterReg::findInstance(event["name"], event["key"]);
+ if (! LLFloater::isShown(floater))
+ {
+ reply["type"] = "LLFloater";
+ reply["name"] = event["name"];
+ reply["key"] = event["key"];
+ reply["error"] = floater? "!isShown()" : "NULL";
+ }
+ else
+ {
+ // Here 'floater' points to an LLFloater instance with the specified
+ // name and key which isShown().
+ LLButton* button = floater->findChild<LLButton>(event["button"]);
+ if (! LLButton::isAvailable(button))
+ {
+ reply["type"] = "LLButton";
+ reply["name"] = event["button"];
+ reply["error"] = button? "!isAvailable()" : "NULL";
+ }
+ else
+ {
+ // Here 'button' points to an isAvailable() LLButton child of
+ // 'floater' with the specified button name. Pretend to click it.
+ button->onCommit();
+ // Leave reply["error"] isUndefined(): no error, i.e. success.
+ }
+ }
+
+ // Send a reply only if caller asked for a reply.
+ LLSD replyPump(event["reply"]);
+ if (replyPump.isString()) // isUndefined() if absent
+ {
+ LLEventPumps::instance().obtain(replyPump).post(reply);
+ }
+}
diff --git a/indra/llui/llfloaterreglistener.h b/indra/llui/llfloaterreglistener.h
new file mode 100644
index 0000000000..586656667c
--- /dev/null
+++ b/indra/llui/llfloaterreglistener.h
@@ -0,0 +1,53 @@
+/**
+ * @file llfloaterreglistener.h
+ * @author Nat Goodspeed
+ * @date 2009-08-12
+ * @brief Wrap (subset of) LLFloaterReg API with an event API
+ *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#if ! defined(LL_LLFLOATERREGLISTENER_H)
+#define LL_LLFLOATERREGLISTENER_H
+
+#include "lleventapi.h"
+#include <string>
+
+class LLSD;
+
+/// Event API wrapper for LLFloaterReg
+class LLFloaterRegListener: public LLEventAPI
+{
+public:
+ /// As all public LLFloaterReg methods are static, there's no point in
+ /// binding an LLFloaterReg instance.
+ LLFloaterRegListener();
+
+private:
+ void getBuildMap(const LLSD& event) const;
+ void showInstance(const LLSD& event) const;
+ void hideInstance(const LLSD& event) const;
+ void toggleInstance(const LLSD& event) const;
+ void clickButton(const LLSD& event) const;
+};
+
+#endif /* ! defined(LL_LLFLOATERREGLISTENER_H) */
diff --git a/indra/llui/llflyoutbutton.cpp b/indra/llui/llflyoutbutton.cpp
index 62a321dc02..4b3a0a5d21 100644
--- a/indra/llui/llflyoutbutton.cpp
+++ b/indra/llui/llflyoutbutton.cpp
@@ -2,31 +2,25 @@
* @file llflyoutbutton.cpp
* @brief LLFlyoutButton base class
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -35,7 +29,7 @@
// file includes
#include "llflyoutbutton.h"
-static LLDefaultWidgetRegistry::Register<LLFlyoutButton> r2("flyout_button");
+//static LLDefaultChildRegistry::Register<LLFlyoutButton> r2("flyout_button");
const S32 FLYOUT_BUTTON_ARROW_WIDTH = 24;
@@ -48,17 +42,13 @@ LLFlyoutButton::LLFlyoutButton(const Params& p)
// Text label button
LLButton::Params bp(p.action_button);
bp.name(p.label);
+ bp.label(p.label);
bp.rect.left(0).bottom(0).width(getRect().getWidth() - FLYOUT_BUTTON_ARROW_WIDTH).height(getRect().getHeight());
bp.click_callback.function(boost::bind(&LLFlyoutButton::onActionButtonClick, this, _2));
bp.follows.flags(FOLLOWS_ALL);
mActionButton = LLUICtrlFactory::create<LLButton>(bp);
addChild(mActionButton);
-
- mButton->setOrigin(getRect().getWidth() - FLYOUT_BUTTON_ARROW_WIDTH, 0);
- mButton->reshape(FLYOUT_BUTTON_ARROW_WIDTH, getRect().getHeight());
- mButton->setFollows(FOLLOWS_RIGHT | FOLLOWS_TOP | FOLLOWS_BOTTOM);
- mButton->setImageOverlay(mListPosition == BELOW ? "down_arrow.tga" : "up_arrow.tga", LLFontGL::RIGHT);
}
void LLFlyoutButton::onActionButtonClick(const LLSD& data)
@@ -75,17 +65,10 @@ void LLFlyoutButton::draw()
//FIXME: this should be an attribute of comboboxes, whether they have a distinct label or
// the label reflects the last selected item, for now we have to manually remove the label
- mButton->setLabel(LLStringUtil::null);
+ setLabel(LLStringUtil::null);
LLComboBox::draw();
}
-void LLFlyoutButton::setEnabled(BOOL enabled)
-{
- mActionButton->setEnabled(enabled);
- LLComboBox::setEnabled(enabled);
-}
-
-
void LLFlyoutButton::setToggleState(BOOL state)
{
mToggleState = state;
diff --git a/indra/llui/llflyoutbutton.h b/indra/llui/llflyoutbutton.h
index f60fe1eb35..8d59380a00 100644
--- a/indra/llui/llflyoutbutton.h
+++ b/indra/llui/llflyoutbutton.h
@@ -2,31 +2,25 @@
* @file llflyoutbutton.h
* @brief LLFlyoutButton base class
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -46,10 +40,14 @@ public:
struct Params : public LLInitParam::Block<Params, LLComboBox::Params>
{
Optional<LLButton::Params> action_button;
+ Deprecated allow_text_entry;
Params()
- : action_button("action_button")
- {}
+ : action_button("action_button"),
+ allow_text_entry("allow_text_entry")
+ {
+ LLComboBox::Params::allow_text_entry = false;
+ }
};
protected:
@@ -57,7 +55,6 @@ protected:
friend class LLUICtrlFactory;
public:
virtual void draw();
- virtual void setEnabled(BOOL enabled);
void setToggleState(BOOL state);
diff --git a/indra/llui/llfocusmgr.cpp b/indra/llui/llfocusmgr.cpp
index 9a4ec7627e..1f16d12add 100644
--- a/indra/llui/llfocusmgr.cpp
+++ b/indra/llui/llfocusmgr.cpp
@@ -2,31 +2,25 @@
* @file llfocusmgr.cpp
* @brief LLFocusMgr base class
*
- * $LicenseInfo:firstyear=2002&license=viewergpl$
- *
- * Copyright (c) 2002-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -38,29 +32,103 @@
const F32 FOCUS_FADE_TIME = 0.3f;
+LLFocusableElement::LLFocusableElement()
+: mFocusLostCallback(NULL),
+ mFocusReceivedCallback(NULL),
+ mFocusChangedCallback(NULL),
+ mTopLostCallback(NULL)
+{
+}
+
+// virtual
+BOOL LLFocusableElement::handleKey(KEY key, MASK mask, BOOL called_from_parent)
+{
+ return FALSE;
+}
+
+// virtual
+BOOL LLFocusableElement::handleUnicodeChar(llwchar uni_char, BOOL called_from_parent)
+{
+ return FALSE;
+}
+
+// virtual
+LLFocusableElement::~LLFocusableElement()
+{
+ delete mFocusLostCallback;
+ delete mFocusReceivedCallback;
+ delete mFocusChangedCallback;
+ delete mTopLostCallback;
+}
+
+void LLFocusableElement::onFocusReceived()
+{
+ if (mFocusReceivedCallback) (*mFocusReceivedCallback)(this);
+ if (mFocusChangedCallback) (*mFocusChangedCallback)(this);
+}
+
+void LLFocusableElement::onFocusLost()
+{
+ if (mFocusLostCallback) (*mFocusLostCallback)(this);
+ if (mFocusChangedCallback) (*mFocusChangedCallback)(this);
+}
+
+void LLFocusableElement::onTopLost()
+{
+ if (mTopLostCallback) (*mTopLostCallback)(this);
+}
+
+BOOL LLFocusableElement::hasFocus() const
+{
+ return gFocusMgr.getKeyboardFocus() == this;
+}
+
+void LLFocusableElement::setFocus(BOOL b)
+{
+}
+
+boost::signals2::connection LLFocusableElement::setFocusLostCallback( const focus_signal_t::slot_type& cb)
+{
+ if (!mFocusLostCallback) mFocusLostCallback = new focus_signal_t();
+ return mFocusLostCallback->connect(cb);
+}
+
+boost::signals2::connection LLFocusableElement::setFocusReceivedCallback(const focus_signal_t::slot_type& cb)
+{
+ if (!mFocusReceivedCallback) mFocusReceivedCallback = new focus_signal_t();
+ return mFocusReceivedCallback->connect(cb);
+}
+
+boost::signals2::connection LLFocusableElement::setFocusChangedCallback(const focus_signal_t::slot_type& cb)
+{
+ if (!mFocusChangedCallback) mFocusChangedCallback = new focus_signal_t();
+ return mFocusChangedCallback->connect(cb);
+}
+
+boost::signals2::connection LLFocusableElement::setTopLostCallback(const focus_signal_t::slot_type& cb)
+{
+ if (!mTopLostCallback) mTopLostCallback = new focus_signal_t();
+ return mTopLostCallback->connect(cb);
+}
+
+
+
LLFocusMgr gFocusMgr;
LLFocusMgr::LLFocusMgr()
- :
- mLockedView( NULL ),
+: mLockedView( NULL ),
mMouseCaptor( NULL ),
mKeyboardFocus( NULL ),
mLastKeyboardFocus( NULL ),
mDefaultKeyboardFocus( NULL ),
mKeystrokesOnly(FALSE),
mTopCtrl( NULL ),
- mFocusWeight(0.f),
mAppHasFocus(TRUE) // Macs don't seem to notify us that we've gotten focus, so default to true
- #ifdef _DEBUG
- , mMouseCaptorName("none")
- , mKeyboardFocusName("none")
- , mTopCtrlName("none")
- #endif
{
}
-void LLFocusMgr::releaseFocusIfNeeded( const LLView* view )
+void LLFocusMgr::releaseFocusIfNeeded( LLView* view )
{
if( childHasMouseCapture( view ) )
{
@@ -80,26 +148,29 @@ void LLFocusMgr::releaseFocusIfNeeded( const LLView* view )
}
}
- if( childIsTopCtrl( view ) )
- {
- setTopCtrl( NULL );
- }
+ LLUI::removePopup(view);
}
-void LLFocusMgr::setKeyboardFocus(LLUICtrl* new_focus, BOOL lock, BOOL keystrokes_only)
+void LLFocusMgr::setKeyboardFocus(LLFocusableElement* new_focus, BOOL lock, BOOL keystrokes_only)
{
+ // notes if keyboard focus is changed again (by onFocusLost/onFocusReceived)
+ // making the rest of our processing unnecessary since it will already be
+ // handled by the recursive call
+ static bool focus_dirty;
+ focus_dirty = false;
+
if (mLockedView &&
(new_focus == NULL ||
- (new_focus != mLockedView && !new_focus->hasAncestor(mLockedView))))
+ (new_focus != mLockedView
+ && dynamic_cast<LLView*>(new_focus)
+ && !dynamic_cast<LLView*>(new_focus)->hasAncestor(mLockedView))))
{
// don't allow focus to go to anything that is not the locked focus
// or one of its descendants
return;
}
- //llinfos << "Keyboard focus handled by " << (new_focus ? new_focus->getName() : "nothing") << llendl;
-
mKeystrokesOnly = keystrokes_only;
if( new_focus != mKeyboardFocus )
@@ -107,22 +178,58 @@ void LLFocusMgr::setKeyboardFocus(LLUICtrl* new_focus, BOOL lock, BOOL keystroke
mLastKeyboardFocus = mKeyboardFocus;
mKeyboardFocus = new_focus;
- if( mLastKeyboardFocus )
+ // list of the focus and it's ancestors
+ view_handle_list_t old_focus_list = mCachedKeyboardFocusList;
+ view_handle_list_t new_focus_list;
+
+ // walk up the tree to root and add all views to the new_focus_list
+ for (LLView* ctrl = dynamic_cast<LLView*>(mKeyboardFocus); ctrl; ctrl = ctrl->getParent())
{
- mLastKeyboardFocus->onFocusLost();
+ new_focus_list.push_back(ctrl->getHandle());
}
- // clear out any existing flash
- if (new_focus)
+ // remove all common ancestors since their focus is unchanged
+ while (!new_focus_list.empty() &&
+ !old_focus_list.empty() &&
+ new_focus_list.back() == old_focus_list.back())
{
- mFocusWeight = 0.f;
- new_focus->onFocusReceived();
+ new_focus_list.pop_back();
+ old_focus_list.pop_back();
+ }
+
+ // walk up the old focus branch calling onFocusLost
+ // we bubble up the tree to release focus, and back down to add
+ for (view_handle_list_t::iterator old_focus_iter = old_focus_list.begin();
+ old_focus_iter != old_focus_list.end() && !focus_dirty;
+ old_focus_iter++)
+ {
+ LLView* old_focus_view = old_focus_iter->get();
+ if (old_focus_view)
+ {
+ mCachedKeyboardFocusList.pop_front();
+ old_focus_view->onFocusLost();
+ }
}
- mFocusTimer.reset();
- #ifdef _DEBUG
- mKeyboardFocusName = new_focus ? new_focus->getName() : std::string("none");
- #endif
+ // walk down the new focus branch calling onFocusReceived
+ for (view_handle_list_t::reverse_iterator new_focus_riter = new_focus_list.rbegin();
+ new_focus_riter != new_focus_list.rend() && !focus_dirty;
+ new_focus_riter++)
+ {
+ LLView* new_focus_view = new_focus_riter->get();
+ if (new_focus_view)
+ {
+ mCachedKeyboardFocusList.push_front(new_focus_view->getHandle());
+ new_focus_view->onFocusReceived();
+ }
+ }
+
+ // if focus was changed as part of an onFocusLost or onFocusReceived call
+ // stop iterating on current list since it is now invalid
+ if (focus_dirty)
+ {
+ return;
+ }
// If we've got a default keyboard focus, and the caller is
// releasing keyboard focus, move to the default.
@@ -131,8 +238,8 @@ void LLFocusMgr::setKeyboardFocus(LLUICtrl* new_focus, BOOL lock, BOOL keystroke
mDefaultKeyboardFocus->setFocus(TRUE);
}
- LLView* focus_subtree = mKeyboardFocus;
- LLView* viewp = mKeyboardFocus;
+ LLView* focus_subtree = dynamic_cast<LLView*>(mKeyboardFocus);
+ LLView* viewp = dynamic_cast<LLView*>(mKeyboardFocus);
// find root-most focus root
while(viewp)
{
@@ -146,7 +253,8 @@ void LLFocusMgr::setKeyboardFocus(LLUICtrl* new_focus, BOOL lock, BOOL keystroke
if (focus_subtree)
{
- mFocusHistory[focus_subtree->getHandle()] = mKeyboardFocus ? mKeyboardFocus->getHandle() : LLHandle<LLView>();
+ LLView* focused_view = dynamic_cast<LLView*>(mKeyboardFocus);
+ mFocusHistory[focus_subtree->getHandle()] = focused_view ? focused_view->getHandle() : LLHandle<LLView>();
}
}
@@ -154,13 +262,15 @@ void LLFocusMgr::setKeyboardFocus(LLUICtrl* new_focus, BOOL lock, BOOL keystroke
{
lockFocus();
}
+
+ focus_dirty = true;
}
// Returns TRUE is parent or any descedent of parent has keyboard focus.
BOOL LLFocusMgr::childHasKeyboardFocus(const LLView* parent ) const
{
- LLView* focus_view = mKeyboardFocus;
+ LLView* focus_view = dynamic_cast<LLView*>(mKeyboardFocus);
while( focus_view )
{
if( focus_view == parent )
@@ -175,7 +285,7 @@ BOOL LLFocusMgr::childHasKeyboardFocus(const LLView* parent ) const
// Returns TRUE is parent or any descedent of parent is the mouse captor.
BOOL LLFocusMgr::childHasMouseCapture( const LLView* parent ) const
{
- if( mMouseCaptor && mMouseCaptor->isView() )
+ if( mMouseCaptor && dynamic_cast<LLView*>(mMouseCaptor) != NULL )
{
LLView* captor_view = (LLView*)mMouseCaptor;
while( captor_view )
@@ -190,7 +300,7 @@ BOOL LLFocusMgr::childHasMouseCapture( const LLView* parent ) const
return FALSE;
}
-void LLFocusMgr::removeKeyboardFocusWithoutCallback( const LLView* focus )
+void LLFocusMgr::removeKeyboardFocusWithoutCallback( const LLFocusableElement* focus )
{
// should be ok to unlock here, as you have to know the locked view
// in order to unlock it
@@ -202,20 +312,12 @@ void LLFocusMgr::removeKeyboardFocusWithoutCallback( const LLView* focus )
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;
@@ -238,24 +340,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
}
}
@@ -284,10 +376,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();
@@ -300,15 +388,12 @@ void LLFocusMgr::removeTopCtrlWithoutCallback( const LLUICtrl* top_view )
if( mTopCtrl == top_view )
{
mTopCtrl = NULL;
- #ifdef _DEBUG
- mTopCtrlName = std::string("none");
- #endif
}
}
void LLFocusMgr::lockFocus()
{
- mLockedView = mKeyboardFocus;
+ mLockedView = dynamic_cast<LLUICtrl*>(mKeyboardFocus);
}
void LLFocusMgr::unlockFocus()
@@ -318,12 +403,12 @@ void LLFocusMgr::unlockFocus()
F32 LLFocusMgr::getFocusFlashAmt() const
{
- return clamp_rescale(getFocusTime(), 0.f, FOCUS_FADE_TIME, mFocusWeight, 0.f);
+ return clamp_rescale(mFocusFlashTimer.getElapsedTimeF32(), 0.f, FOCUS_FADE_TIME, 1.f, 0.f);
}
LLColor4 LLFocusMgr::getFocusColor() const
{
- static LLUICachedControl<LLColor4> focus_color_cached ("FocusColor", *(new LLColor4));
+ static LLUIColor focus_color_cached = LLUIColorTable::instance().getColor("FocusColor");
LLColor4 focus_color = lerp(focus_color_cached, LLColor4::white, getFocusFlashAmt());
// de-emphasize keyboard focus when app has lost focus (to avoid typing into wrong window problem)
if (!mAppHasFocus)
@@ -335,8 +420,7 @@ LLColor4 LLFocusMgr::getFocusColor() const
void LLFocusMgr::triggerFocusFlash()
{
- mFocusTimer.reset();
- mFocusWeight = 1.f;
+ mFocusFlashTimer.reset();
}
void LLFocusMgr::setAppHasFocus(BOOL focus)
@@ -347,9 +431,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 aaeb25a870..eef82a3b5a 100644
--- a/indra/llui/llfocusmgr.h
+++ b/indra/llui/llfocusmgr.h
@@ -2,31 +2,25 @@
* @file llfocusmgr.h
* @brief LLFocusMgr base class
*
- * $LicenseInfo:firstyear=2002&license=viewergpl$
- *
- * Copyright (c) 2002-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -37,10 +31,44 @@
#include "llstring.h"
#include "llframetimer.h"
-#include "llview.h"
+#include "llui.h"
class LLUICtrl;
class LLMouseHandler;
+class LLView;
+
+// NOTE: the LLFocusableElement class declaration has been moved here from lluictrl.h.
+class LLFocusableElement
+{
+ friend class LLFocusMgr; // allow access to focus change handlers
+public:
+ LLFocusableElement();
+ virtual ~LLFocusableElement();
+
+ virtual void setFocus( BOOL b );
+ virtual BOOL hasFocus() const;
+
+ typedef boost::signals2::signal<void(LLFocusableElement*)> focus_signal_t;
+
+ boost::signals2::connection setFocusLostCallback( const focus_signal_t::slot_type& cb);
+ boost::signals2::connection setFocusReceivedCallback(const focus_signal_t::slot_type& cb);
+ boost::signals2::connection setFocusChangedCallback(const focus_signal_t::slot_type& cb);
+ boost::signals2::connection setTopLostCallback(const focus_signal_t::slot_type& cb);
+
+ // These were brought up the hierarchy from LLView so that we don't have to use dynamic_cast when dealing with keyboard focus.
+ virtual BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent);
+ virtual BOOL handleUnicodeChar(llwchar uni_char, BOOL called_from_parent);
+
+ virtual void onTopLost(); // called when registered as top ctrl and user clicks elsewhere
+protected:
+ virtual void onFocusReceived();
+ virtual void onFocusLost();
+ focus_signal_t* mFocusLostCallback;
+ focus_signal_t* mFocusReceivedCallback;
+ focus_signal_t* mFocusChangedCallback;
+ focus_signal_t* mTopLostCallback;
+};
+
class LLFocusMgr
{
@@ -55,15 +83,14 @@ public:
BOOL childHasMouseCapture( const LLView* parent ) const;
// Keyboard Focus
- void setKeyboardFocus(LLUICtrl* new_focus, BOOL lock = FALSE, BOOL keystrokes_only = FALSE); // new_focus = NULL to release the focus.
- LLUICtrl* getKeyboardFocus() const { return mKeyboardFocus; }
- LLUICtrl* getLastKeyboardFocus() const { return mLastKeyboardFocus; }
+ void setKeyboardFocus(LLFocusableElement* new_focus, BOOL lock = FALSE, BOOL keystrokes_only = FALSE); // new_focus = NULL to release the focus.
+ LLFocusableElement* getKeyboardFocus() const { return mKeyboardFocus; }
+ LLFocusableElement* getLastKeyboardFocus() const { return mLastKeyboardFocus; }
BOOL childHasKeyboardFocus( const LLView* parent ) const;
- void removeKeyboardFocusWithoutCallback( const LLView* focus );
+ void removeKeyboardFocusWithoutCallback( const LLFocusableElement* focus );
BOOL getKeystrokesOnly() { return mKeystrokesOnly; }
void setKeystrokesOnly(BOOL keystrokes_only) { mKeystrokesOnly = keystrokes_only; }
- F32 getFocusTime() const { return mFocusTimer.getElapsedTimeF32(); }
F32 getFocusFlashAmt() const;
S32 getFocusFlashWidth() const { return llround(lerp(1.f, 3.f, getFocusFlashAmt())); }
LLColor4 getFocusColor() const;
@@ -75,8 +102,8 @@ public:
// If setKeyboardFocus(NULL) is called, and there is a non-NULL default
// keyboard focus view, focus goes there. JC
- void setDefaultKeyboardFocus(LLUICtrl* default_focus) { mDefaultKeyboardFocus = default_focus; }
- LLUICtrl* getDefaultKeyboardFocus() const { return mDefaultKeyboardFocus; }
+ void setDefaultKeyboardFocus(LLFocusableElement* default_focus) { mDefaultKeyboardFocus = default_focus; }
+ LLFocusableElement* getDefaultKeyboardFocus() const { return mDefaultKeyboardFocus; }
// Top View
@@ -86,7 +113,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; }
@@ -98,27 +125,24 @@ private:
LLMouseHandler* mMouseCaptor; // Mouse events are premptively routed to this object
// Keyboard Focus
- LLUICtrl* mKeyboardFocus; // Keyboard events are preemptively routed to this object
- LLUICtrl* mLastKeyboardFocus; // who last had focus
- LLUICtrl* mDefaultKeyboardFocus;
+ LLFocusableElement* mKeyboardFocus; // Keyboard events are preemptively routed to this object
+ LLFocusableElement* mLastKeyboardFocus; // who last had focus
+ LLFocusableElement* mDefaultKeyboardFocus;
BOOL mKeystrokesOnly;
+
+ // caching list of keyboard focus ancestors for calling onFocusReceived and onFocusLost
+ typedef std::list<LLHandle<LLView> > view_handle_list_t;
+ view_handle_list_t mCachedKeyboardFocusList;
// Top View
LLUICtrl* mTopCtrl;
- LLFrameTimer mFocusTimer;
- F32 mFocusWeight;
+ LLFrameTimer mFocusFlashTimer;
BOOL mAppHasFocus;
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/llfunctorregistry.cpp b/indra/llui/llfunctorregistry.cpp
index 0c5b1655b1..8003324973 100644
--- a/indra/llui/llfunctorregistry.cpp
+++ b/indra/llui/llfunctorregistry.cpp
@@ -3,34 +3,29 @@
* @author Kent Quirk
* @brief Maintains a registry of named callback functors taking a single LLSD parameter
*
- * $LicenseInfo:firstyear=2008&license=viewergpl$
- *
- * Copyright (c) 2008-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2008&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
**/
+#include "linden_common.h"
#include "llfunctorregistry.h"
// This is a default functor always resident in the system.
diff --git a/indra/llui/llfunctorregistry.h b/indra/llui/llfunctorregistry.h
index 2c0bcc6012..752c7df7ee 100644
--- a/indra/llui/llfunctorregistry.h
+++ b/indra/llui/llfunctorregistry.h
@@ -3,31 +3,25 @@
* @author Kent Quirk
* @brief Maintains a registry of named callback functors taking a single LLSD parameter
*
- * $LicenseInfo:firstyear=2008&license=viewergpl$
- *
- * Copyright (c) 2008-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2008&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llui/llhandle.h b/indra/llui/llhandle.h
new file mode 100644
index 0000000000..a43f095d67
--- /dev/null
+++ b/indra/llui/llhandle.h
@@ -0,0 +1,172 @@
+/**
+* @file llhandle.h
+* @brief "Handle" to an object (usually a floater) whose lifetime you don't
+* control.
+*
+* $LicenseInfo:firstyear=2001&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2010, Linden Research, Inc.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation;
+* version 2.1 of the License only.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+* $/LicenseInfo$
+*/
+#ifndef LLHANDLE_H
+#define LLHANDLE_H
+
+#include "llpointer.h"
+
+template <typename T>
+class LLTombStone : public LLRefCount
+{
+public:
+ LLTombStone(T* target = NULL) : mTarget(target) {}
+
+ void setTarget(T* target) { mTarget = target; }
+ T* getTarget() const { return mTarget; }
+private:
+ T* mTarget;
+};
+
+// LLHandles are used to refer to objects whose lifetime you do not control or influence.
+// Calling get() on a handle will return a pointer to the referenced object or NULL,
+// if the object no longer exists. Note that during the lifetime of the returned pointer,
+// you are assuming that the object will not be deleted by any action you perform,
+// or any other thread, as normal when using pointers, so avoid using that pointer outside of
+// the local code block.
+//
+// https://wiki.lindenlab.com/mediawiki/index.php?title=LLHandle&oldid=79669
+
+template <typename T>
+class LLHandle
+{
+public:
+ LLHandle() : mTombStone(getDefaultTombStone()) {}
+ const LLHandle<T>& operator =(const LLHandle<T>& other)
+ {
+ mTombStone = other.mTombStone;
+ return *this;
+ }
+
+ template<typename Subclass>
+ LLHandle<T>& operator =(const LLHandle<Subclass>& other)
+ {
+ mTombStone = other.mTombStone;
+ return *this;
+ }
+
+ bool isDead() const
+ {
+ return mTombStone->getTarget() == NULL;
+ }
+
+ void markDead()
+ {
+ mTombStone = getDefaultTombStone();
+ }
+
+ T* get() const
+ {
+ return mTombStone->getTarget();
+ }
+
+ friend bool operator== (const LLHandle<T>& lhs, const LLHandle<T>& rhs)
+ {
+ return lhs.mTombStone == rhs.mTombStone;
+ }
+ friend bool operator!= (const LLHandle<T>& lhs, const LLHandle<T>& rhs)
+ {
+ return !(lhs == rhs);
+ }
+ friend bool operator< (const LLHandle<T>& lhs, const LLHandle<T>& rhs)
+ {
+ return lhs.mTombStone < rhs.mTombStone;
+ }
+ friend bool operator> (const LLHandle<T>& lhs, const LLHandle<T>& rhs)
+ {
+ return lhs.mTombStone > rhs.mTombStone;
+ }
+protected:
+
+protected:
+ LLPointer<LLTombStone<T> > mTombStone;
+
+private:
+ static LLPointer<LLTombStone<T> >& getDefaultTombStone()
+ {
+ static LLPointer<LLTombStone<T> > sDefaultTombStone = new LLTombStone<T>;
+ return sDefaultTombStone;
+ }
+};
+
+template <typename T>
+class LLRootHandle : public LLHandle<T>
+{
+public:
+ LLRootHandle(T* object) { bind(object); }
+ LLRootHandle() {};
+ ~LLRootHandle() { unbind(); }
+
+ // this is redundant, since a LLRootHandle *is* an LLHandle
+ LLHandle<T> getHandle() { return LLHandle<T>(*this); }
+
+ void bind(T* object)
+ {
+ // unbind existing tombstone
+ if (LLHandle<T>::mTombStone.notNull())
+ {
+ if (LLHandle<T>::mTombStone->getTarget() == object) return;
+ LLHandle<T>::mTombStone->setTarget(NULL);
+ }
+ // tombstone reference counted, so no paired delete
+ LLHandle<T>::mTombStone = new LLTombStone<T>(object);
+ }
+
+ void unbind()
+ {
+ LLHandle<T>::mTombStone->setTarget(NULL);
+ }
+
+ //don't allow copying of root handles, since there should only be one
+private:
+ LLRootHandle(const LLRootHandle& other) {};
+};
+
+// Use this as a mixin for simple classes that need handles and when you don't
+// want handles at multiple points of the inheritance hierarchy
+template <typename T>
+class LLHandleProvider
+{
+protected:
+ typedef LLHandle<T> handle_type_t;
+ LLHandleProvider()
+ {
+ // provided here to enforce T deriving from LLHandleProvider<T>
+ }
+
+ LLHandle<T> getHandle()
+ {
+ // perform lazy binding to avoid small tombstone allocations for handle
+ // providers whose handles are never referenced
+ mHandle.bind(static_cast<T*>(this));
+ return mHandle;
+ }
+
+private:
+ LLRootHandle<T> mHandle;
+};
+
+#endif
diff --git a/indra/llui/llhelp.h b/indra/llui/llhelp.h
new file mode 100644
index 0000000000..83317bd03c
--- /dev/null
+++ b/indra/llui/llhelp.h
@@ -0,0 +1,43 @@
+/**
+ * @file llhelp.h
+ * @brief Abstract interface to the Help system
+ * @author Tofu Linden
+ *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLHELP_H
+#define LL_LLHELP_H
+
+class LLHelp
+{
+ public:
+ virtual void showTopic(const std::string &topic) = 0;
+ // return default (fallback) topic name suitable for showTopic()
+ virtual std::string defaultTopic() = 0;
+ // return topic to use before the user logs in
+ virtual std::string preLoginTopic() = 0;
+ // return topic to use for the top-level help, invoked by F1
+ virtual std::string f1HelpTopic() = 0;
+};
+
+#endif // headerguard
diff --git a/indra/llui/lliconctrl.cpp b/indra/llui/lliconctrl.cpp
index eddfc71284..627957061d 100644
--- a/indra/llui/lliconctrl.cpp
+++ b/indra/llui/lliconctrl.cpp
@@ -2,31 +2,25 @@
* @file lliconctrl.cpp
* @brief LLIconCtrl base class
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -42,7 +36,7 @@
#include "lluictrlfactory.h"
#include "lluiimage.h"
-static LLDefaultWidgetRegistry::Register<LLIconCtrl> r("icon");
+static LLDefaultChildRegistry::Register<LLIconCtrl> r("icon");
LLIconCtrl::Params::Params()
: image("image_name"),
@@ -56,7 +50,10 @@ LLIconCtrl::Params::Params()
LLIconCtrl::LLIconCtrl(const LLIconCtrl::Params& p)
: LLUICtrl(p),
mColor(p.color()),
- mImagep(p.image)
+ mImagep(p.image),
+ mPriority(0),
+ mDrawWidth(0),
+ mDrawHeight(0)
{
if (mImagep.notNull())
{
@@ -74,7 +71,7 @@ void LLIconCtrl::draw()
{
if( mImagep.notNull() )
{
- mImagep->draw(getLocalRect(), mColor.get() );
+ mImagep->draw(getLocalRect(), mColor.get() % getDrawContext().mAlpha );
}
LLUICtrl::draw();
@@ -93,12 +90,14 @@ void LLIconCtrl::setValue(const LLSD& value )
LLUICtrl::setValue(tvalue);
if (tvalue.isUUID())
{
- mImagep = LLUI::getUIImageByID(tvalue.asUUID());
+ mImagep = LLUI::getUIImageByID(tvalue.asUUID(), mPriority);
}
else
{
- mImagep = LLUI::getUIImage(tvalue.asString());
+ mImagep = LLUI::getUIImage(tvalue.asString(), mPriority);
}
+
+ setIconImageDrawSize();
}
std::string LLIconCtrl::getImageName() const
@@ -108,3 +107,15 @@ std::string LLIconCtrl::getImageName() const
else
return std::string();
}
+
+void LLIconCtrl::setIconImageDrawSize()
+{
+ if(mImagep.notNull() && mDrawWidth && mDrawHeight)
+ {
+ if(mImagep->getImage().notNull())
+ {
+ mImagep->getImage()->setKnownDrawSize(mDrawWidth, mDrawHeight) ;
+ }
+ }
+}
+
diff --git a/indra/llui/lliconctrl.h b/indra/llui/lliconctrl.h
index ad0f6f563f..79a8b0fb28 100644
--- a/indra/llui/lliconctrl.h
+++ b/indra/llui/lliconctrl.h
@@ -2,31 +2,25 @@
* @file lliconctrl.h
* @brief LLIconCtrl base class
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -37,7 +31,6 @@
#include "v4color.h"
#include "lluictrl.h"
#include "stdenums.h"
-#include "llimagegl.h"
class LLTextBox;
class LLUICtrlFactory;
@@ -55,12 +48,13 @@ public:
{
Optional<LLUIImage*> image;
Optional<LLUIColor> color;
- Deprecated scale_image;
+ Ignored scale_image;
Params();
};
protected:
LLIconCtrl(const Params&);
friend class LLUICtrlFactory;
+
public:
virtual ~LLIconCtrl();
@@ -73,10 +67,21 @@ public:
std::string getImageName() const;
void setColor(const LLColor4& color) { mColor = color; }
+ void setImage(LLPointer<LLUIImage> image) { mImagep = image; }
+
+private:
+ void setIconImageDrawSize() ;
+
+protected:
+ S32 mPriority;
+
+ //the output size of the icon image if set.
+ S32 mDrawWidth ;
+ S32 mDrawHeight ;
private:
LLUIColor mColor;
- LLPointer<LLUIImage> mImagep;
+ LLPointer<LLUIImage> mImagep;
};
#endif
diff --git a/indra/llui/llkeywords.cpp b/indra/llui/llkeywords.cpp
index 30796a5ab9..ceec9c7eb1 100644
--- a/indra/llui/llkeywords.cpp
+++ b/indra/llui/llkeywords.cpp
@@ -2,31 +2,25 @@
* @file llkeywords.cpp
* @brief Keyword list for LSL
*
- * $LicenseInfo:firstyear=2000&license=viewergpl$
- *
- * Copyright (c) 2000-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2000&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -218,6 +212,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 )
{
@@ -231,11 +305,13 @@ LLColor3 LLKeywords::readColor( const std::string& s )
return LLColor3( r, g, b );
}
+LLFastTimer::DeclareTimer FTM_SYNTAX_COLORING("Syntax Coloring");
+
// Walk through a string, applying the rules specified by the keyword token list and
// create a list of color segments.
-void LLKeywords::findSegments(std::vector<LLTextSegment *>* seg_list, const LLWString& wtext, const LLColor4 &defaultColor)
+void LLKeywords::findSegments(std::vector<LLTextSegmentPtr>* seg_list, const LLWString& wtext, const LLColor4 &defaultColor, LLTextEditor& editor)
{
- std::for_each(seg_list->begin(), seg_list->end(), DeletePointer());
+ LLFastTimer ft(FTM_SYNTAX_COLORING);
seg_list->clear();
if( wtext.empty() )
@@ -243,9 +319,9 @@ void LLKeywords::findSegments(std::vector<LLTextSegment *>* seg_list, const LLWS
return;
}
- S32 text_len = wtext.size();
+ S32 text_len = wtext.size() + 1;
- seg_list->push_back( new LLTextSegment( LLColor3(defaultColor), 0, text_len ) );
+ seg_list->push_back( new LLNormalTextSegment( defaultColor, 0, text_len, editor ) );
const llwchar* base = wtext.c_str();
const llwchar* cur = base;
@@ -257,6 +333,9 @@ void LLKeywords::findSegments(std::vector<LLTextSegment *>* seg_list, const LLWS
{
if( *cur == '\n' )
{
+ LLTextSegmentPtr text_segment = new LLLineBreakTextSegment(cur-base);
+ text_segment->setToken( 0 );
+ insertSegment( *seg_list, text_segment, text_len, defaultColor, editor);
cur++;
if( !*cur || *cur == '\n' )
{
@@ -296,9 +375,8 @@ void LLKeywords::findSegments(std::vector<LLTextSegment *>* seg_list, const LLWS
}
S32 seg_end = cur - base;
- LLTextSegment* text_segment = new LLTextSegment( cur_token->getColor(), seg_start, seg_end );
- text_segment->setToken( cur_token );
- insertSegment( seg_list, text_segment, text_len, defaultColor);
+ //create segments from seg_start to seg_end
+ insertSegments(wtext, *seg_list,cur_token, text_len, seg_start, seg_end, defaultColor, editor);
line_done = TRUE; // to break out of second loop.
break;
}
@@ -404,11 +482,12 @@ void LLKeywords::findSegments(std::vector<LLTextSegment *>* seg_list, const LLWS
seg_end = seg_start + between_delimiters + cur_delimiter->getLength();
}
-
- LLTextSegment* text_segment = new LLTextSegment( cur_delimiter->getColor(), seg_start, seg_end );
+ insertSegments(wtext, *seg_list,cur_delimiter, text_len, seg_start, seg_end, defaultColor, editor);
+ /*
+ LLTextSegmentPtr text_segment = new LLNormalTextSegment( cur_delimiter->getColor(), seg_start, seg_end, editor );
text_segment->setToken( cur_delimiter );
- insertSegment( seg_list, text_segment, text_len, defaultColor);
-
+ insertSegment( seg_list, text_segment, text_len, defaultColor, editor);
+ */
// Note: we don't increment cur, since the end of one delimited seg may be immediately
// followed by the start of another one.
continue;
@@ -427,7 +506,7 @@ void LLKeywords::findSegments(std::vector<LLTextSegment *>* seg_list, const LLWS
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() )
{
@@ -437,10 +516,7 @@ void LLKeywords::findSegments(std::vector<LLTextSegment *>* seg_list, const LLWS
// llinfos << "Seg: [" << word.c_str() << "]" << llendl;
-
- LLTextSegment* text_segment = new LLTextSegment( cur_token->getColor(), seg_start, seg_end );
- text_segment->setToken( cur_token );
- insertSegment( seg_list, text_segment, text_len, defaultColor);
+ insertSegments(wtext, *seg_list,cur_token, text_len, seg_start, seg_end, defaultColor, editor);
}
cur += seg_len;
continue;
@@ -455,25 +531,50 @@ void LLKeywords::findSegments(std::vector<LLTextSegment *>* seg_list, const LLWS
}
}
-void LLKeywords::insertSegment(std::vector<LLTextSegment*>* seg_list, LLTextSegment* new_segment, S32 text_len, const LLColor4 &defaultColor )
+void LLKeywords::insertSegments(const LLWString& wtext, std::vector<LLTextSegmentPtr>& seg_list, LLKeywordToken* cur_token, S32 text_len, S32 seg_start, S32 seg_end, const LLColor4 &defaultColor, LLTextEditor& editor )
+{
+ std::string::size_type pos = wtext.find('\n',seg_start);
+
+ while (pos!=-1 && pos < (std::string::size_type)seg_end)
+ {
+ if (pos!=seg_start)
+ {
+ LLTextSegmentPtr text_segment = new LLNormalTextSegment( cur_token->getColor(), seg_start, pos, editor );
+ text_segment->setToken( cur_token );
+ insertSegment( seg_list, text_segment, text_len, defaultColor, editor);
+ }
+
+ LLTextSegmentPtr text_segment = new LLLineBreakTextSegment(pos);
+ text_segment->setToken( cur_token );
+ insertSegment( seg_list, text_segment, text_len, defaultColor, editor);
+
+ seg_start = pos+1;
+ pos = wtext.find('\n',seg_start);
+ }
+
+ LLTextSegmentPtr text_segment = new LLNormalTextSegment( cur_token->getColor(), seg_start, seg_end, editor );
+ text_segment->setToken( cur_token );
+ insertSegment( seg_list, text_segment, text_len, defaultColor, editor);
+}
+
+void LLKeywords::insertSegment(std::vector<LLTextSegmentPtr>& seg_list, LLTextSegmentPtr new_segment, S32 text_len, const LLColor4 &defaultColor, LLTextEditor& editor )
{
- LLTextSegment* last = seg_list->back();
+ LLTextSegmentPtr last = seg_list.back();
S32 new_seg_end = new_segment->getEnd();
if( new_segment->getStart() == last->getStart() )
{
- *last = *new_segment;
- delete new_segment;
+ seg_list.pop_back();
}
else
{
last->setEnd( new_segment->getStart() );
- seg_list->push_back( new_segment );
}
+ seg_list.push_back( new_segment );
if( new_seg_end < text_len )
{
- seg_list->push_back( new LLTextSegment( defaultColor, new_seg_end, text_len ) );
+ seg_list.push_back( new LLNormalTextSegment( defaultColor, new_seg_end, text_len, editor ) );
}
}
diff --git a/indra/llui/llkeywords.h b/indra/llui/llkeywords.h
index 38f5e993c2..f6d75b7e75 100644
--- a/indra/llui/llkeywords.h
+++ b/indra/llui/llkeywords.h
@@ -2,31 +2,25 @@
* @file llkeywords.h
* @brief Keyword list for LSL
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -39,9 +33,10 @@
#include <map>
#include <list>
#include <deque>
+#include "llpointer.h"
class LLTextSegment;
-
+typedef LLPointer<LLTextSegment> LLTextSegmentPtr;
class LLKeywordToken
{
@@ -84,15 +79,40 @@ public:
BOOL loadFromFile(const std::string& filename);
BOOL isLoaded() const { return mLoaded; }
- void findSegments(std::vector<LLTextSegment *> *seg_list, const LLWString& text, const LLColor4 &defaultColor );
+ void findSegments(std::vector<LLTextSegmentPtr> *seg_list, const LLWString& text, const LLColor4 &defaultColor, class LLTextEditor& editor );
// Add the token as described
void addToken(LLKeywordToken::TOKEN_TYPE type,
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(); }
@@ -103,7 +123,8 @@ public:
private:
LLColor3 readColor(const std::string& s);
- void insertSegment(std::vector<LLTextSegment *> *seg_list, LLTextSegment* new_segment, S32 text_len, const LLColor4 &defaultColor);
+ void insertSegment(std::vector<LLTextSegmentPtr>& seg_list, LLTextSegmentPtr new_segment, S32 text_len, const LLColor4 &defaultColor, class LLTextEditor& editor);
+ void insertSegments(const LLWString& wtext, std::vector<LLTextSegmentPtr>& seg_list, LLKeywordToken* token, S32 text_len, S32 seg_start, S32 seg_end, const LLColor4 &defaultColor, LLTextEditor& editor);
BOOL mLoaded;
word_token_map_t mWordTokenMap;
diff --git a/indra/llui/lllayoutstack.cpp b/indra/llui/lllayoutstack.cpp
index 39dac296ea..0ff7557ead 100644
--- a/indra/llui/lllayoutstack.cpp
+++ b/indra/llui/lllayoutstack.cpp
@@ -2,31 +2,25 @@
* @file lllayoutstack.cpp
* @brief LLLayout class - dynamic stacking of UI elements
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -35,10 +29,13 @@
#include "linden_common.h"
#include "lllayoutstack.h"
+
+#include "lllocalcliprect.h"
+#include "llpanel.h"
#include "llresizebar.h"
#include "llcriticaldamp.h"
-static LLDefaultWidgetRegistry::Register<LLLayoutStack> register_layout_stack("layout_stack", &LLLayoutStack::fromXML);
+static LLDefaultChildRegistry::Register<LLLayoutStack> register_layout_stack("layout_stack", &LLLayoutStack::fromXML);
//
@@ -46,9 +43,11 @@ static LLDefaultWidgetRegistry::Register<LLLayoutStack> register_layout_stack("l
//
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),
@@ -109,6 +108,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;
@@ -120,7 +124,8 @@ struct LLLayoutStack::LayoutPanel
LLLayoutStack::Params::Params()
: orientation("orientation", std::string("vertical")),
- animate("animate", TRUE),
+ animate("animate", true),
+ clip("clip", true),
border_size("border_size", LLCachedControl<S32>(*LLUI::sSettingGroups["config"], "UIResizeBarHeight", 0))
{
name="stack";
@@ -132,7 +137,9 @@ LLLayoutStack::LLLayoutStack(const LLLayoutStack::Params& p)
mMinHeight(0),
mPanelSpacing(p.border_size),
mOrientation((p.orientation() == "vertical") ? VERTICAL : HORIZONTAL),
- mAnimate(p.animate)
+ mAnimate(p.animate),
+ mAnimatedThisFrame(false),
+ mClip(p.clip)
{}
LLLayoutStack::~LLLayoutStack()
@@ -163,10 +170,11 @@ void LLLayoutStack::draw()
LLPanel* panelp = (*panel_it)->mPanel;
- LLLocalClipRect clip(clip_rect);
+ LLLocalClipRect clip(clip_rect, mClip);
// only force drawing invisible children if visible amount is non-zero
- drawChild(panelp, 0, 0, !clip_rect.isNull());
+ drawChild(panelp, 0, 0, !clip_rect.isEmpty());
}
+ mAnimatedThisFrame = false;
}
void LLLayoutStack::removeChild(LLView* view)
@@ -223,8 +231,8 @@ static void get_attribute_bool_and_write(LLXMLNodePtr node,
//static
LLView* LLLayoutStack::fromXML(LLXMLNodePtr node, LLView *parent, LLXMLNodePtr output_node)
{
- LLLayoutStack::Params p(LLUICtrlFactory::getDefaultParams<LLLayoutStack::Params>());
- LLXUIParser::instance().readXUI(node, p);
+ LLLayoutStack::Params p(LLUICtrlFactory::getDefaultParams<LLLayoutStack>());
+ LLXUIParser::instance().readXUI(node, p, LLUICtrlFactory::getInstance()->getCurFileName());
// Export must happen before setupParams() mungles rectangles and before
// this item gets added to parent (otherwise screws up last_child_rect
@@ -233,13 +241,14 @@ LLView* LLLayoutStack::fromXML(LLXMLNodePtr node, LLView *parent, LLXMLNodePtr o
{
Params output_params(p);
setupParamsForExport(output_params, parent);
- LLLayoutStack::Params default_params(LLUICtrlFactory::getDefaultParams<LLLayoutStack::Params>());
+ LLLayoutStack::Params default_params(LLUICtrlFactory::getDefaultParams<LLLayoutStack>());
output_node->setName(node->getName()->mString);
LLXUIParser::instance().writeXUI(
output_node, output_params, &default_params);
}
- setupParams(p, parent);
+ p.from_xui = true;
+ applyXUILayout(p, parent);
LLLayoutStack* layout_stackp = LLUICtrlFactory::create<LLLayoutStack>(p);
if (parent && layout_stackp)
@@ -253,10 +262,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;
@@ -273,6 +286,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);
@@ -285,7 +302,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
@@ -295,12 +312,13 @@ LLView* LLLayoutStack::fromXML(LLXMLNodePtr node, LLView *parent, LLXMLNodePtr o
FALSE, output_child);
LLPanel::Params p;
+ p.mouse_opaque(false);
LLPanel* panelp = LLUICtrlFactory::create<LLPanel>(p);
- LLView* new_child = LLUICtrlFactory::getInstance()->createFromXML(child_node, panelp, LLStringUtil::null, output_child, parent ? parent->getChildRegistry() : LLDefaultWidgetRegistry::instance());
+ LLView* new_child = LLUICtrlFactory::getInstance()->createFromXML(child_node, panelp, LLStringUtil::null, LLPanel::child_registry_t::instance(), output_child);
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);
@@ -350,14 +368,30 @@ 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::movePanel(LLPanel* panel_to_move, LLPanel* target_panel, bool move_to_front)
+{
+ LayoutPanel* embedded_panel_to_move = findEmbeddedPanel(panel_to_move);
+ LayoutPanel* embedded_target_panel = move_to_front ? *mPanels.begin() : findEmbeddedPanel(target_panel);
+
+ if (!embedded_panel_to_move || !embedded_target_panel || embedded_panel_to_move == embedded_target_panel)
+ {
+ llwarns << "One of the panels was not found in stack or NULL was passed instead of valid panel" << llendl;
+ return;
+ }
+ e_panel_list_t::iterator it = std::find(mPanels.begin(), mPanels.end(), embedded_panel_to_move);
+ mPanels.erase(it);
+ it = move_to_front ? mPanels.begin() : std::find(mPanels.begin(), mPanels.end(), embedded_target_panel);
+ mPanels.insert(it, embedded_panel_to_move);
+}
+
+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);
@@ -395,8 +429,56 @@ void LLLayoutStack::collapsePanel(LLPanel* panel, BOOL collapsed)
panel_container->mCollapsed = collapsed;
}
+void LLLayoutStack::updatePanelAutoResize(const std::string& panel_name, BOOL auto_resize)
+{
+ LayoutPanel* panel = findEmbeddedPanelByName(panel_name);
+
+ if (panel)
+ {
+ panel->mAutoResize = auto_resize;
+ }
+}
+
+void LLLayoutStack::setPanelUserResize(const std::string& panel_name, BOOL user_resize)
+{
+ LayoutPanel* panel = findEmbeddedPanelByName(panel_name);
+
+ if (panel)
+ {
+ panel->mUserResize = user_resize;
+ }
+}
+
+bool LLLayoutStack::getPanelMinSize(const std::string& panel_name, S32* min_widthp, S32* min_heightp)
+{
+ LayoutPanel* panel = findEmbeddedPanelByName(panel_name);
+
+ if (panel)
+ {
+ if (min_widthp) *min_widthp = panel->mMinWidth;
+ if (min_heightp) *min_heightp = panel->mMinHeight;
+ }
+
+ 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)
{
+ LLFastTimer ft(FTM_UPDATE_LAYOUT);
static LLUICachedControl<S32> resize_bar_overlap ("UIResizeBarOverlap", 0);
calcMinExtents();
@@ -415,10 +497,13 @@ void LLLayoutStack::updateLayout(BOOL force_resize)
{
if (mAnimate)
{
- (*panel_it)->mVisibleAmt = lerp((*panel_it)->mVisibleAmt, 1.f, LLCriticalDamp::getInterpolant(ANIM_OPEN_TIME));
- if ((*panel_it)->mVisibleAmt > 0.99f)
+ if (!mAnimatedThisFrame)
{
- (*panel_it)->mVisibleAmt = 1.f;
+ (*panel_it)->mVisibleAmt = lerp((*panel_it)->mVisibleAmt, 1.f, LLCriticalDamp::getInterpolant(ANIM_OPEN_TIME));
+ if ((*panel_it)->mVisibleAmt > 0.99f)
+ {
+ (*panel_it)->mVisibleAmt = 1.f;
+ }
}
}
else
@@ -430,10 +515,13 @@ void LLLayoutStack::updateLayout(BOOL force_resize)
{
if (mAnimate)
{
- (*panel_it)->mVisibleAmt = lerp((*panel_it)->mVisibleAmt, 0.f, LLCriticalDamp::getInterpolant(ANIM_CLOSE_TIME));
- if ((*panel_it)->mVisibleAmt < 0.001f)
+ if (!mAnimatedThisFrame)
{
- (*panel_it)->mVisibleAmt = 0.f;
+ (*panel_it)->mVisibleAmt = lerp((*panel_it)->mVisibleAmt, 0.f, LLCriticalDamp::getInterpolant(ANIM_CLOSE_TIME));
+ if ((*panel_it)->mVisibleAmt < 0.001f)
+ {
+ (*panel_it)->mVisibleAmt = 0.f;
+ }
}
}
else
@@ -615,10 +703,10 @@ void LLLayoutStack::updateLayout(BOOL force_resize)
// adjust running headroom count based on new sizes
shrink_headroom_total += delta_size;
- panelp->reshape(new_width, new_height);
- panelp->setOrigin(cur_x, cur_y - new_height);
+ LLRect panel_rect;
+ panel_rect.setLeftTopAndSize(cur_x, cur_y, new_width, new_height);
+ panelp->setShape(panel_rect);
- LLRect panel_rect = panelp->getRect();
LLRect resize_bar_rect = panel_rect;
if (mOrientation == HORIZONTAL)
{
@@ -689,6 +777,8 @@ void LLLayoutStack::updateLayout(BOOL force_resize)
llassert_always(force_resize == FALSE);
updateLayout(TRUE);
}
+
+ mAnimatedThisFrame = true;
} // end LLLayoutStack::updateLayout
@@ -707,6 +797,24 @@ LLLayoutStack::LayoutPanel* LLLayoutStack::findEmbeddedPanel(LLPanel* panelp) co
return NULL;
}
+LLLayoutStack::LayoutPanel* LLLayoutStack::findEmbeddedPanelByName(const std::string& name) const
+{
+ LayoutPanel* result = NULL;
+
+ for (e_panel_list_t::const_iterator panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it)
+ {
+ LayoutPanel* p = *panel_it;
+
+ if (p->mPanel->getName() == name)
+ {
+ result = p;
+ break;
+ }
+ }
+
+ return result;
+}
+
// Compute sum of min_width or min_height of children
void LLLayoutStack::calcMinExtents()
{
@@ -738,3 +846,19 @@ void LLLayoutStack::calcMinExtents()
}
}
}
+
+// update layout stack animations, etc. once per frame
+// NOTE: we use this to size world view based on animating UI, *before* we draw the UI
+// we might still need to call updateLayout during UI draw phase, in case UI elements
+// are resizing themselves dynamically
+//static
+void LLLayoutStack::updateClass()
+{
+ 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 480bdb5c17..6fcc8e2ac3 100644
--- a/indra/llui/lllayoutstack.h
+++ b/indra/llui/lllayoutstack.h
@@ -3,48 +3,44 @@
* @author Richard Nelson
* @brief LLLayout class - dynamic stacking of UI elements
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LL_LLLAYOUTSTACK_H
#define LL_LLLAYOUTSTACK_H
-#include "llpanel.h"
+#include "llview.h"
-class LLLayoutStack : public LLView
+class LLPanel;
+
+class LLLayoutStack : public LLView, public LLInstanceTracker<LLLayoutStack>
{
public:
struct Params : public LLInitParam::Block<Params, LLView::Params>
{
Optional<std::string> orientation;
Optional<S32> border_size;
- Optional<bool> animate;
- // mMinWidth and mMinHeight are calculated, not set in XML
+ Optional<bool> animate,
+ clip;
Params();
};
@@ -72,10 +68,42 @@ 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(); }
+ /**
+ * Moves panel_to_move before target_panel inside layout stack (both panels should already be there).
+ * If move_to_front is true target_panel is ignored and panel_to_move is moved to the beginning of mPanels
+ */
+ void movePanel(LLPanel* panel_to_move, LLPanel* target_panel, bool move_to_front = false);
+
+ void updatePanelAutoResize(const std::string& panel_name, BOOL auto_resize);
+ void setPanelUserResize(const std::string& panel_name, BOOL user_resize);
+
+ /**
+ * Gets minimal 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 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);
+
+ S32 getPanelSpacing() const { return mPanelSpacing; }
+ BOOL getAnimate () const { return mAnimate; }
+ void setAnimate (BOOL animate) { mAnimate = animate; }
+
+ static void updateClass();
protected:
LLLayoutStack(const Params&);
@@ -84,7 +112,6 @@ protected:
private:
struct LayoutPanel;
- void updateLayout(BOOL force_resize = FALSE);
void calcMinExtents();
S32 getDefaultHeight(S32 cur_height);
S32 getDefaultWidth(S32 cur_width);
@@ -93,13 +120,18 @@ private:
typedef std::vector<LayoutPanel*> e_panel_list_t;
e_panel_list_t mPanels;
+
LayoutPanel* findEmbeddedPanel(LLPanel* panelp) const;
+ LayoutPanel* findEmbeddedPanelByName(const std::string& name) const;
S32 mMinWidth; // calculated by calcMinExtents
S32 mMinHeight; // calculated by calcMinExtents
S32 mPanelSpacing;
+ // true if we already applied animation this frame
+ bool mAnimatedThisFrame;
bool mAnimate;
+ bool mClip;
}; // end class LLLayoutStack
#endif
diff --git a/indra/llui/lllazyvalue.h b/indra/llui/lllazyvalue.h
index cf45214628..0fc95d9efa 100644
--- a/indra/llui/lllazyvalue.h
+++ b/indra/llui/lllazyvalue.h
@@ -3,31 +3,25 @@
* @brief generic functor/value abstraction for lazy evaluation of a value
* parsing construction parameters from xml and LLSD
*
- * $LicenseInfo:firstyear=2008&license=viewergpl$
- *
- * Copyright (c) 2008-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2008&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp
index 5ea45e13cf..2759167d04 100644
--- a/indra/llui/lllineeditor.cpp
+++ b/indra/llui/lllineeditor.cpp
@@ -2,31 +2,25 @@
* @file lllineeditor.cpp
* @brief LLLineEditor base class
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -34,12 +28,10 @@
#include "linden_common.h"
-#define INSTANTIATE_GETCHILD_LINEEDITOR
-
+#define LLLINEEDITOR_CPP
#include "lllineeditor.h"
#include "lltexteditor.h"
-#include "audioengine.h"
#include "llmath.h"
#include "llfontgl.h"
#include "llgl.h"
@@ -57,6 +49,7 @@
#include "llui.h"
#include "lluictrlfactory.h"
#include "llclipboard.h"
+#include "llmenugl.h"
//
// Imported globals
@@ -72,45 +65,36 @@ 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?
-static LLDefaultWidgetRegistry::Register<LLLineEditor> r1("line_editor");
+const std::string PASSWORD_ASTERISK( "\xE2\x80\xA2" ); // U+2022 BULLET
+
+static LLDefaultChildRegistry::Register<LLLineEditor> r1("line_editor");
-template LLLineEditor* LLView::getChild<LLLineEditor>( const std::string& name, BOOL recurse, BOOL create_if_missing ) const;
+// Compiler optimization, generate extern template
+template class LLLineEditor* LLView::getChild<class LLLineEditor>(
+ const std::string& name, BOOL recurse) const;
//
// 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("printable_not_pipe", LLLineEditor::prevalidatePrintableNotPipe);
- declare("printable_no_space", LLLineEditor::prevalidatePrintableNoSpace);
-}
-
LLLineEditor::Params::Params()
: max_length_bytes("max_length", 254),
+ keystroke_callback("keystroke_callback"),
+ prevalidate_callback("prevalidate_callback"),
background_image("background_image"),
+ 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),
cursor_color("cursor_color"),
text_color("text_color"),
text_readonly_color("text_readonly_color"),
text_tentative_color("text_tentative_color"),
- bg_readonly_color("bg_readonly_color"),
- bg_writeable_color("bg_writeable_color"),
- bg_focus_color("bg_focus_color"),
+ highlight_color("highlight_color"),
+ preedit_bg_color("preedit_bg_color"),
border(""),
- is_unicode("is_unicode"),
- drop_shadow_visible("drop_shadow_visible"),
- border_drop_shadow_visible("border_drop_shadow_visible"),
bg_visible("bg_visible"),
text_pad_left("text_pad_left"),
text_pad_right("text_pad_right"),
@@ -119,6 +103,7 @@ LLLineEditor::Params::Params()
mouse_opaque = true;
addSynonym(select_on_focus, "select_all_on_focus_received");
addSynonym(border, "border");
+ addSynonym(label, "watermark_text");
}
LLLineEditor::LLLineEditor(const LLLineEditor::Params& p)
@@ -128,8 +113,10 @@ LLLineEditor::LLLineEditor(const LLLineEditor::Params& p)
mScrollHPos( 0 ),
mTextPadLeft(p.text_pad_left),
mTextPadRight(p.text_pad_right),
+ mTextLeftEdge(0), // computed in updateTextPadding() below
+ mTextRightEdge(0), // computed in updateTextPadding() below
mCommitOnFocusLost( p.commit_on_focus_lost ),
- mRevertOnEsc( TRUE ),
+ mRevertOnEsc( p.revert_on_esc ),
mKeystrokeCallback( p.keystroke_callback() ),
mIsSelecting( FALSE ),
mSelectionStart( 0 ),
@@ -142,22 +129,23 @@ 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),
- mImage( NULL ),
+ mBgImage( p.background_image ),
+ mBgImageDisabled( p.background_image_disabled ),
+ mBgImageFocused( p.background_image_focused ),
+ mHaveHistory(FALSE),
mReplaceNewlinesWithSpaces( TRUE ),
mLabel(p.label),
mCursorColor(p.cursor_color()),
mFgColor(p.text_color()),
mReadOnlyFgColor(p.text_readonly_color()),
mTentativeFgColor(p.text_tentative_color()),
- mWriteableBgColor(p.bg_writeable_color()),
- mReadOnlyBgColor(p.bg_readonly_color()),
- mFocusBgColor(p.bg_focus_color()),
+ mHighlightColor(p.highlight_color()),
+ mPreeditBgColor(p.preedit_bg_color()),
mGLFont(p.font),
- mGLFontStyle(LLFontGL::getStyleFromString(p.font.style))
+ mContextMenuHandle()
{
llassert( mMaxLengthBytes > 0 );
@@ -165,13 +153,8 @@ LLLineEditor::LLLineEditor(const LLLineEditor::Params& p)
mTripleClickTimer.reset();
setText(p.default_text());
- // line history support:
- // - initialize line history list
- mLineHistory.insert( mLineHistory.end(), "" );
- // - disable line history by default
- mHaveHistory = FALSE;
- // - reset current history line pointer
- mCurrentHistoryLine = 0;
+ // Initialize current history line iterator
+ mCurrentHistoryLine = mLineHistory.begin();
LLRect border_rect(getLocalRect());
// adjust for gl line drawing glitch
@@ -180,7 +163,7 @@ LLLineEditor::LLLineEditor(const LLLineEditor::Params& p)
LLViewBorder::Params border_p(p.border);
border_p.rect = border_rect;
border_p.follows.flags = FOLLOWS_ALL;
- border_p.bevel_type = LLViewBorder::BEVEL_IN;
+ border_p.bevel_style = LLViewBorder::BEVEL_IN;
mBorder = LLUICtrlFactory::create<LLViewBorder>(border_p);
addChild( mBorder );
@@ -189,18 +172,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;
- }
}
@@ -233,6 +218,7 @@ void LLLineEditor::onFocusLost()
LLUICtrl::onFocusLost();
}
+// virtual
void LLLineEditor::onCommit()
{
// put current line into the line history
@@ -243,6 +229,33 @@ void LLLineEditor::onCommit()
selectAll();
}
+// Returns TRUE if user changed value at all
+// virtual
+BOOL LLLineEditor::isDirty() const
+{
+ return mText.getString() != mPrevText;
+}
+
+// Clear dirty state
+// virtual
+void LLLineEditor::resetDirty()
+{
+ mPrevText = mText.getString();
+}
+
+// assumes UTF8 text
+// virtual
+void LLLineEditor::setValue(const LLSD& value )
+{
+ setText(value.asString());
+}
+
+//virtual
+LLSD LLLineEditor::getValue() const
+{
+ return LLSD(getText());
+}
+
// line history support
void LLLineEditor::updateHistory()
@@ -251,16 +264,31 @@ void LLLineEditor::updateHistory()
// reset current history line number.
// Be sure only to remember lines that are not empty and that are
// different from the last on the list.
- if( mHaveHistory && mText.length() && ( mLineHistory.empty() || getText() != mLineHistory.back() ) )
+ if( mHaveHistory && getLength() )
{
- // discard possible empty line at the end of the history
- // inserted by setText()
- if( !mLineHistory.back().length() )
+ if( !mLineHistory.empty() )
+ {
+ // When not empty, last line of history should always be blank.
+ if( mLineHistory.back().empty() )
+ {
+ // discard the empty line
+ mLineHistory.pop_back();
+ }
+ else
+ {
+ LL_WARNS("") << "Last line of history was not blank." << LL_ENDL;
+ }
+ }
+
+ // Add text to history, ignoring duplicates
+ if( mLineHistory.empty() || getText() != mLineHistory.back() )
{
- mLineHistory.pop_back();
+ mLineHistory.push_back( getText() );
}
- mLineHistory.insert( mLineHistory.end(), getText() );
- mCurrentHistoryLine = mLineHistory.size() - 1;
+
+ // Restore the blank line and set mCurrentHistoryLine to point at it
+ mLineHistory.push_back( "" );
+ mCurrentHistoryLine = mLineHistory.end() - 1;
}
}
@@ -285,13 +313,23 @@ void LLLineEditor::setMaxTextLength(S32 max_text_length)
mMaxLengthBytes = max_len;
}
+void LLLineEditor::getTextPadding(S32 *left, S32 *right)
+{
+ *left = mTextPadLeft;
+ *right = mTextPadRight;
+}
+
+void LLLineEditor::setTextPadding(S32 left, S32 right)
+{
+ mTextPadLeft = left;
+ mTextPadRight = right;
+ updateTextPadding();
+}
+
void LLLineEditor::updateTextPadding()
{
- static LLUICachedControl<S32> line_editor_hpad ("UILineEditorHPad", 0);
- mTextPadLeft = llclamp(mTextPadLeft, 0, getRect().getWidth());
- mTextPadRight = llclamp(mTextPadRight, 0, getRect().getWidth());
- mMinHPixels = line_editor_hpad + mTextPadLeft;
- mMaxHPixels = getRect().getWidth() - mMinHPixels - mTextPadRight;
+ mTextLeftEdge = llclamp(mTextPadLeft, 0, getRect().getWidth());
+ mTextRightEdge = getRect().getWidth() - llclamp(mTextPadRight, 0, getRect().getWidth());
}
@@ -332,11 +370,11 @@ void LLLineEditor::setText(const LLStringExplicit &new_text)
}
setCursor(llmin((S32)mText.length(), getCursor()));
- // Newly set text goes always in the last line of history.
- // Possible empty strings (as with chat line) will be deleted later.
- mLineHistory.insert( mLineHistory.end(), new_text );
// Set current history line to end of history.
- mCurrentHistoryLine = mLineHistory.size() - 1;
+ if(mLineHistory.end() != mLineHistory.begin())
+ {
+ mCurrentHistoryLine = mLineHistory.end() - 1;
+ }
mPrevText = mText;
}
@@ -351,7 +389,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();
}
@@ -360,8 +398,8 @@ void LLLineEditor::setCursorAtLocalPos( S32 local_mouse_x )
mScrollHPos +
mGLFont->charFromPixelOffset(
wtext, mScrollHPos,
- (F32)(local_mouse_x - mMinHPixels),
- (F32)(mMaxHPixels - mMinHPixels + 1)); // min-max range is inclusive
+ (F32)(local_mouse_x - mTextLeftEdge),
+ (F32)(mTextRightEdge - mTextLeftEdge + 1)); // min-max range is inclusive
setCursor(cursor_pos);
}
@@ -370,12 +408,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 > mMaxHPixels )
+ 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)(mMaxHPixels - mMinHPixels + width_chars_to_left)));
- S32 min_scroll = mGLFont->firstDrawableChar(mText.getWString().c_str(), (F32)(mMaxHPixels - mMinHPixels), mText.length(), getCursor());
+ S32 last_visible_char = mGLFont->maxDrawableChars(mText.getWString().c_str(), llmax(0.f, (F32)(mTextRightEdge - mTextLeftEdge + width_chars_to_left)));
+ // character immediately to left of cursor should be last one visible (SCROLL_INCREMENT_ADD will scroll in more characters)
+ // or first character if cursor is at beginning
+ S32 new_last_visible_char = llmax(0, getCursor() - 1);
+ S32 min_scroll = mGLFont->firstDrawableChar(mText.getWString().c_str(), (F32)(mTextRightEdge - mTextLeftEdge), mText.length(), new_last_visible_char);
if (old_cursor_pos == last_visible_char)
{
mScrollHPos = llmin(mText.length(), llmax(min_scroll, mScrollHPos + SCROLL_INCREMENT_ADD));
@@ -446,6 +488,7 @@ void LLLineEditor::selectAll()
setCursor(mSelectionEnd);
//mScrollHPos = 0;
mIsSelecting = TRUE;
+ updatePrimary();
}
@@ -466,19 +509,19 @@ BOOL LLLineEditor::handleDoubleClick(S32 x, S32 y, MASK mask)
BOOL doSelectAll = TRUE;
// Select the word we're on
- if( LLTextEditor::isPartOfWord( wtext[mCursorPos] ) )
+ if( LLWStringUtil::isPartOfWord( wtext[mCursorPos] ) )
{
S32 old_selection_start = mLastSelectionStart;
S32 old_selection_end = mLastSelectionEnd;
// Select word the cursor is over
- while ((mCursorPos > 0) && LLTextEditor::isPartOfWord( wtext[mCursorPos-1] ))
+ while ((mCursorPos > 0) && LLWStringUtil::isPartOfWord( wtext[mCursorPos-1] ))
{ // Find the start of the word
mCursorPos--;
}
startSelection();
- while ((mCursorPos < (S32)wtext.length()) && LLTextEditor::isPartOfWord( wtext[mCursorPos] ) )
+ while ((mCursorPos < (S32)wtext.length()) && LLWStringUtil::isPartOfWord( wtext[mCursorPos] ) )
{ // Find the end of the word
mCursorPos++;
}
@@ -516,18 +559,13 @@ BOOL LLLineEditor::handleMouseDown(S32 x, S32 y, MASK mask)
{
return TRUE;
}
- if (mSelectAllonFocusReceived
- && gFocusMgr.getKeyboardFocus() != this)
- {
- setFocus( TRUE );
- }
- else
+
+ if (!mSelectAllonFocusReceived
+ || gFocusMgr.getKeyboardFocus() == this)
{
mLastSelectionStart = -1;
mLastSelectionStart = -1;
- setFocus( TRUE );
-
if (mask & MASK_SHIFT)
{
// Handle selection extension
@@ -593,8 +631,13 @@ BOOL LLLineEditor::handleMouseDown(S32 x, S32 y, MASK mask)
gFocusMgr.setMouseCapture( this );
}
+ setFocus(TRUE);
+
// delay cursor flashing
mKeystrokeTimer.reset();
+
+ if (mMouseDownSignal)
+ (*mMouseDownSignal)(this,x,y,mask);
return TRUE;
}
@@ -611,6 +654,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;
@@ -636,17 +689,17 @@ BOOL LLLineEditor::handleHover(S32 x, S32 y, MASK mask)
S32 increment = llround(mScrollTimer.getElapsedTimeF32() / AUTO_SCROLL_TIME);
mScrollTimer.reset();
mScrollTimer.setTimerExpirySec(AUTO_SCROLL_TIME);
- if( (x < mMinHPixels) && (mScrollHPos > 0 ) )
+ if( (x < mTextLeftEdge) && (mScrollHPos > 0 ) )
{
// Scroll to the left
mScrollHPos = llclamp(mScrollHPos - increment, 0, mText.length());
}
else
- if( (x > mMaxHPixels) && (mCursorPos < (S32)mText.length()) )
+ if( (x > mTextRightEdge) && (mCursorPos < (S32)mText.length()) )
{
// If scrolling one pixel would make a difference...
S32 pixels_after_scrolling_one_char = findPixelNearestPos(1);
- if( pixels_after_scrolling_one_char >= mMaxHPixels )
+ if( pixels_after_scrolling_one_char >= mTextRightEdge )
{
// ...scroll to the right
mScrollHPos = llclamp(mScrollHPos + increment, 0, mText.length());
@@ -708,7 +761,10 @@ BOOL LLLineEditor::handleMouseUp(S32 x, S32 y, MASK mask)
// take selection to 'primary' clipboard
updatePrimary();
}
-
+
+ // We won't call LLUICtrl::handleMouseUp to avoid double calls of childrenHandleMouseUp().Just invoke the signal manually.
+ if (mMouseUpSignal)
+ (*mMouseUpSignal)(this,x,y, mask);
return handled;
}
@@ -724,7 +780,7 @@ void LLLineEditor::removeChar()
}
else
{
- reportBadKeystroke();
+ LLUI::reportBadKeystroke();
}
}
@@ -763,7 +819,7 @@ void LLLineEditor::addChar(const llwchar uni_char)
}
else
{
- reportBadKeystroke();
+ LLUI::reportBadKeystroke();
}
getWindow()->hideCursorUntilMouseMove();
@@ -808,7 +864,7 @@ S32 LLLineEditor::prevWordPos(S32 cursorPos) const
{
cursorPos--;
}
- while( (cursorPos > 0) && LLTextEditor::isPartOfWord( wtext[cursorPos-1] ) )
+ while( (cursorPos > 0) && LLWStringUtil::isPartOfWord( wtext[cursorPos-1] ) )
{
cursorPos--;
}
@@ -818,7 +874,7 @@ S32 LLLineEditor::prevWordPos(S32 cursorPos) const
S32 LLLineEditor::nextWordPos(S32 cursorPos) const
{
const LLWString& wtext = mText.getWString();
- while( (cursorPos < getLength()) && LLTextEditor::isPartOfWord( wtext[cursorPos] ) )
+ while( (cursorPos < getLength()) && LLWStringUtil::isPartOfWord( wtext[cursorPos] ) )
{
cursorPos++;
}
@@ -852,7 +908,7 @@ BOOL LLLineEditor::handleSelectionKey(KEY key, MASK mask)
}
else
{
- reportBadKeystroke();
+ LLUI::reportBadKeystroke();
}
break;
@@ -868,7 +924,7 @@ BOOL LLLineEditor::handleSelectionKey(KEY key, MASK mask)
}
else
{
- reportBadKeystroke();
+ LLUI::reportBadKeystroke();
}
break;
@@ -894,22 +950,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
@@ -956,7 +996,7 @@ void LLLineEditor::cut()
if( need_to_rollback )
{
rollback.doRollback( this );
- reportBadKeystroke();
+ LLUI::reportBadKeystroke();
}
else
if( mKeystrokeCallback )
@@ -1065,7 +1105,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);
@@ -1077,7 +1117,7 @@ void LLLineEditor::pasteHelper(bool is_primary)
if( need_to_rollback )
{
rollback.doRollback( this );
- reportBadKeystroke();
+ LLUI::reportBadKeystroke();
}
else
if( mKeystrokeCallback )
@@ -1142,7 +1182,7 @@ BOOL LLLineEditor::handleSpecialKey(KEY key, MASK mask)
}
else
{
- reportBadKeystroke();
+ LLUI::reportBadKeystroke();
}
}
handled = TRUE;
@@ -1191,7 +1231,7 @@ BOOL LLLineEditor::handleSpecialKey(KEY key, MASK mask)
}
else
{
- reportBadKeystroke();
+ LLUI::reportBadKeystroke();
}
handled = TRUE;
}
@@ -1218,7 +1258,7 @@ BOOL LLLineEditor::handleSpecialKey(KEY key, MASK mask)
}
else
{
- reportBadKeystroke();
+ LLUI::reportBadKeystroke();
}
handled = TRUE;
}
@@ -1228,14 +1268,14 @@ BOOL LLLineEditor::handleSpecialKey(KEY key, MASK mask)
case KEY_UP:
if( mHaveHistory && ( MASK_CONTROL == mask ) )
{
- if( mCurrentHistoryLine > 0 )
+ if( mCurrentHistoryLine > mLineHistory.begin() )
{
- mText.assign( mLineHistory[ --mCurrentHistoryLine ] );
+ mText.assign( *(--mCurrentHistoryLine) );
setCursor(llmin((S32)mText.length(), getCursor()));
}
else
{
- reportBadKeystroke();
+ LLUI::reportBadKeystroke();
}
handled = TRUE;
}
@@ -1245,14 +1285,14 @@ BOOL LLLineEditor::handleSpecialKey(KEY key, MASK mask)
case KEY_DOWN:
if( mHaveHistory && ( MASK_CONTROL == mask ) )
{
- if( !mLineHistory.empty() && mCurrentHistoryLine < mLineHistory.size() - 1 )
+ if( !mLineHistory.empty() && mCurrentHistoryLine < mLineHistory.end() - 1 )
{
- mText.assign( mLineHistory[ ++mCurrentHistoryLine ] );
+ mText.assign( *(++mCurrentHistoryLine) );
setCursor(llmin((S32)mText.length(), getCursor()));
}
else
{
- reportBadKeystroke();
+ LLUI::reportBadKeystroke();
}
handled = TRUE;
}
@@ -1275,64 +1315,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;
}
@@ -1387,7 +1369,7 @@ BOOL LLLineEditor::handleKeyHere(KEY key, MASK mask )
{
rollback.doRollback(this);
- reportBadKeystroke();
+ LLUI::reportBadKeystroke();
}
// Notify owner if requested
@@ -1435,7 +1417,7 @@ BOOL LLLineEditor::handleUnicodeCharHere(llwchar uni_char)
{
rollback.doRollback( this );
- reportBadKeystroke();
+ LLUI::reportBadKeystroke();
}
// Notify owner if requested
@@ -1455,7 +1437,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()
@@ -1480,7 +1462,7 @@ void LLLineEditor::doDelete()
if( need_to_rollback )
{
rollback.doRollback( this );
- reportBadKeystroke();
+ LLUI::reportBadKeystroke();
}
else
{
@@ -1493,11 +1475,43 @@ void LLLineEditor::doDelete()
}
+void LLLineEditor::drawBackground()
+{
+ bool has_focus = hasFocus();
+ LLUIImage* image;
+ if ( mReadOnly )
+ {
+ image = mBgImageDisabled;
+ }
+ else if ( has_focus )
+ {
+ image = mBgImageFocused;
+ }
+ else
+ {
+ image = mBgImage;
+ }
+
+ F32 alpha = getDrawContext().mAlpha;
+ // optionally draw programmatic border
+ if (has_focus)
+ {
+ LLColor4 tmp_color = gFocusMgr.getFocusColor();
+ tmp_color.setAlpha(alpha);
+ image->drawBorder(0, 0, getRect().getWidth(), getRect().getHeight(),
+ tmp_color,
+ gFocusMgr.getFocusFlashWidth());
+ }
+ LLColor4 tmp_color = UI_VERTEX_COLOR;
+ tmp_color.setAlpha(alpha);
+ image->draw(getLocalRect(), tmp_color);
+}
+
void LLLineEditor::draw()
{
+ F32 alpha = getDrawContext().mAlpha;
S32 text_len = mText.length();
static LLUICachedControl<S32> lineeditor_cursor_thickness ("UILineEditorCursorThickness", 0);
- static LLUICachedControl<S32> lineeditor_v_pad ("UILineEditorVPad", 0);
static LLUICachedControl<F32> preedit_marker_brightness ("UIPreeditMarkerBrightness", 0);
static LLUICachedControl<S32> preedit_marker_gap ("UIPreeditMarkerGap", 0);
static LLUICachedControl<S32> preedit_marker_position ("UIPreeditMarkerPosition", 0);
@@ -1514,7 +1528,7 @@ void LLLineEditor::draw()
std::string text;
for (S32 i = 0; i < mText.length(); i++)
{
- text += '*';
+ text += PASSWORD_ASTERISK;
}
mText = text;
}
@@ -1523,37 +1537,14 @@ void LLLineEditor::draw()
LLRect background( 0, getRect().getHeight(), getRect().getWidth(), 0 );
background.stretch( -mBorderThickness );
- LLColor4 bg_color = mReadOnlyBgColor.get();
-
-#if 0 // for when we're ready for image art.
- if( hasFocus())
- {
- mImage->drawBorder(0, 0, getRect().getWidth(), getRect().getHeight(), gFocusMgr.getFocusColor(), gFocusMgr.getFocusFlashWidth());
- }
- mImage->draw(getLocalRect(), mReadOnly ? mReadOnlyBgColor : mWriteableBgColor );
-#else // the old programmer art.
- // drawing solids requires texturing be disabled
- {
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
- // draw background for text
- if( !mReadOnly )
- {
- if( gFocusMgr.getKeyboardFocus() == this )
- {
- bg_color = mFocusBgColor.get();
- }
- else
- {
- bg_color = mWriteableBgColor.get();
- }
- }
- gl_rect_2d(background, bg_color);
- }
-#endif
+ S32 lineeditor_v_pad = llround((background.getHeight() - mGLFont->getLineHeight())/2);
+ drawBackground();
+
// draw text
- S32 cursor_bottom = background.mBottom + 1;
+ // With viewer-2 art files, input region is 2 pixels up
+ S32 cursor_bottom = background.mBottom + 2;
S32 cursor_top = background.mTop - 1;
LLColor4 text_color;
@@ -1572,8 +1563,10 @@ void LLLineEditor::draw()
{
text_color = mReadOnlyFgColor.get();
}
+ text_color.setAlpha(alpha);
LLColor4 label_color = mTentativeFgColor.get();
-
+ label_color.setAlpha(alpha);
+
if (hasPreeditString())
{
// Draw preedit markers. This needs to be before drawing letters.
@@ -1595,7 +1588,8 @@ void LLLineEditor::draw()
background.mBottom + preedit_standout_position,
preedit_pixels_right - preedit_standout_gap - 1,
background.mBottom + preedit_standout_position - preedit_standout_thickness,
- (text_color * preedit_standout_brightness + bg_color * (1 - preedit_standout_brightness)).setAlpha(1.0f));
+ (text_color * preedit_standout_brightness
+ + mPreeditBgColor * (1 - preedit_standout_brightness)).setAlpha(alpha/*1.0f*/));
}
else
{
@@ -1603,14 +1597,15 @@ void LLLineEditor::draw()
background.mBottom + preedit_marker_position,
preedit_pixels_right - preedit_marker_gap - 1,
background.mBottom + preedit_marker_position - preedit_marker_thickness,
- (text_color * preedit_marker_brightness + bg_color * (1 - preedit_marker_brightness)).setAlpha(1.0f));
+ (text_color * preedit_marker_brightness
+ + mPreeditBgColor * (1 - preedit_marker_brightness)).setAlpha(alpha/*1.0f*/));
}
}
}
}
S32 rendered_text = 0;
- F32 rendered_pixels_right = (F32)mMinHPixels;
+ F32 rendered_pixels_right = (F32)mTextLeftEdge;
F32 text_bottom = (F32)background.mBottom + (F32)lineeditor_v_pad;
if( (gFocusMgr.getKeyboardFocus() == this) && hasSelection() )
@@ -1636,34 +1631,36 @@ void LLLineEditor::draw()
rendered_pixels_right, text_bottom,
text_color,
LLFontGL::LEFT, LLFontGL::BOTTOM,
- mGLFontStyle,
+ 0,
LLFontGL::NO_SHADOW,
select_left - mScrollHPos,
- mMaxHPixels - llround(rendered_pixels_right),
+ mTextRightEdge - llround(rendered_pixels_right),
&rendered_pixels_right);
}
- if( (rendered_pixels_right < (F32)mMaxHPixels) && (rendered_text < text_len) )
+ if( (rendered_pixels_right < (F32)mTextRightEdge) && (rendered_text < text_len) )
{
- LLColor4 color(1.f - bg_color.mV[0], 1.f - bg_color.mV[1], 1.f - bg_color.mV[2], 1.f);
+ LLColor4 color = mHighlightColor;
+ color.setAlpha(alpha);
// selected middle
S32 width = mGLFont->getWidth(mText.getWString().c_str(), mScrollHPos + rendered_text, select_right - mScrollHPos - rendered_text);
- width = llmin(width, mMaxHPixels - llround(rendered_pixels_right));
+ width = llmin(width, mTextRightEdge - llround(rendered_pixels_right));
gl_rect_2d(llround(rendered_pixels_right), cursor_top, llround(rendered_pixels_right)+width, cursor_bottom, color);
+ LLColor4 tmp_color( 1.f - text_color.mV[0], 1.f - text_color.mV[1], 1.f - text_color.mV[2], alpha );
rendered_text += mGLFont->render(
mText, mScrollHPos + rendered_text,
rendered_pixels_right, text_bottom,
- LLColor4( 1.f - text_color.mV[0], 1.f - text_color.mV[1], 1.f - text_color.mV[2], 1 ),
+ tmp_color,
LLFontGL::LEFT, LLFontGL::BOTTOM,
- mGLFontStyle,
+ 0,
LLFontGL::NO_SHADOW,
select_right - mScrollHPos - rendered_text,
- mMaxHPixels - llround(rendered_pixels_right),
+ mTextRightEdge - llround(rendered_pixels_right),
&rendered_pixels_right);
}
- if( (rendered_pixels_right < (F32)mMaxHPixels) && (rendered_text < text_len) )
+ if( (rendered_pixels_right < (F32)mTextRightEdge) && (rendered_text < text_len) )
{
// unselected, right side
mGLFont->render(
@@ -1671,10 +1668,10 @@ void LLLineEditor::draw()
rendered_pixels_right, text_bottom,
text_color,
LLFontGL::LEFT, LLFontGL::BOTTOM,
- mGLFontStyle,
+ 0,
LLFontGL::NO_SHADOW,
S32_MAX,
- mMaxHPixels - llround(rendered_pixels_right),
+ mTextRightEdge - llround(rendered_pixels_right),
&rendered_pixels_right);
}
}
@@ -1685,22 +1682,22 @@ void LLLineEditor::draw()
rendered_pixels_right, text_bottom,
text_color,
LLFontGL::LEFT, LLFontGL::BOTTOM,
- mGLFontStyle,
+ 0,
LLFontGL::NO_SHADOW,
S32_MAX,
- mMaxHPixels - llround(rendered_pixels_right),
+ mTextRightEdge - llround(rendered_pixels_right),
&rendered_pixels_right);
}
-#if 0 // for when we're ready for image art.
+#if 1 // for when we're ready for image art.
mBorder->setVisible(FALSE); // no more programmatic art.
#endif
// If we're editing...
- if( gFocusMgr.getKeyboardFocus() == this)
+ if( hasFocus())
{
//mBorder->setVisible(TRUE); // ok, programmer art just this once.
// (Flash the cursor every half second)
- if (gShowTextEditCursor && !mReadOnly)
+ if (!mReadOnly && gFocusMgr.getAppHasFocus())
{
F32 elapsed = mKeystrokeTimer.getElapsedTimeF32();
if( (elapsed < CURSOR_FLASH_DELAY ) || (S32(elapsed * 2) & 1) )
@@ -1720,10 +1717,11 @@ void LLLineEditor::draw()
cursor_right, cursor_bottom, text_color);
if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode() && !hasSelection())
{
+ LLColor4 tmp_color( 1.f - text_color.mV[0], 1.f - text_color.mV[1], 1.f - text_color.mV[2], alpha );
mGLFont->render(mText, getCursor(), (F32)(cursor_left + lineeditor_cursor_thickness / 2), text_bottom,
- LLColor4( 1.f - text_color.mV[0], 1.f - text_color.mV[1], 1.f - text_color.mV[2], 1 ),
+ tmp_color,
LLFontGL::LEFT, LLFontGL::BOTTOM,
- mGLFontStyle,
+ 0,
LLFontGL::NO_SHADOW,
1);
}
@@ -1745,14 +1743,14 @@ void LLLineEditor::draw()
if (0 == mText.length() && mReadOnly)
{
mGLFont->render(mLabel.getWString(), 0,
- mMinHPixels, (F32)text_bottom,
+ mTextLeftEdge, (F32)text_bottom,
label_color,
LLFontGL::LEFT,
LLFontGL::BOTTOM,
- mGLFontStyle,
+ 0,
LLFontGL::NO_SHADOW,
S32_MAX,
- mMaxHPixels - llround(rendered_pixels_right),
+ mTextRightEdge - llround(rendered_pixels_right),
&rendered_pixels_right, FALSE);
}
@@ -1770,14 +1768,14 @@ void LLLineEditor::draw()
if (0 == mText.length())
{
mGLFont->render(mLabel.getWString(), 0,
- mMinHPixels, (F32)text_bottom,
+ mTextLeftEdge, (F32)text_bottom,
label_color,
LLFontGL::LEFT,
LLFontGL::BOTTOM,
- mGLFontStyle,
+ 0,
LLFontGL::NO_SHADOW,
S32_MAX,
- mMaxHPixels - llround(rendered_pixels_right),
+ mTextRightEdge - llround(rendered_pixels_right),
&rendered_pixels_right, FALSE);
}
// Draw children (border)
@@ -1795,15 +1793,10 @@ void LLLineEditor::draw()
S32 LLLineEditor::findPixelNearestPos(const S32 cursor_offset) const
{
S32 dpos = getCursor() - mScrollHPos + cursor_offset;
- S32 result = mGLFont->getWidth(mText.getWString().c_str(), mScrollHPos, dpos) + mMinHPixels;
+ S32 result = mGLFont->getWidth(mText.getWString().c_str(), mScrollHPos, dpos) + mTextLeftEdge;
return result;
}
-void LLLineEditor::reportBadKeystroke()
-{
- make_ui_sound("UISndBadKeystroke");
-}
-
//virtual
void LLLineEditor::clear()
{
@@ -1891,51 +1884,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)
{
@@ -1995,210 +1949,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;
-}
-
-// static
-BOOL LLLineEditor::prevalidatePrintableNotPipe(const LLWString &str)
-{
- BOOL rv = TRUE;
- S32 len = str.length();
- if(len == 0) return rv;
- while(len--)
- {
- if('|' == str[len])
- {
- rv = FALSE;
- break;
- }
- if(!((' ' == str[len]) || LLStringOps::isAlnum((char)str[len]) || LLStringOps::isPunct((char)str[len])))
- {
- rv = FALSE;
- break;
- }
- }
- return rv;
-}
-
-
-// static
-BOOL LLLineEditor::prevalidatePrintableNoSpace(const LLWString &str)
-{
- BOOL rv = TRUE;
- S32 len = str.length();
- if(len == 0) return rv;
- while(len--)
- {
- if(LLStringOps::isSpace(str[len]))
- {
- 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();
@@ -2261,14 +2011,20 @@ BOOL LLLineEditor::hasPreeditString() const
void LLLineEditor::resetPreedit()
{
- if (hasPreeditString())
+ if (hasSelection())
{
- if (hasSelection())
+ if (hasPreeditString())
{
llwarns << "Preedit and selection!" << llendl;
deselect();
}
-
+ else
+ {
+ deleteSelection();
+ }
+ }
+ if (hasPreeditString())
+ {
const S32 preedit_pos = mPreeditPositions.front();
mText.erase(preedit_pos, mPreeditPositions.back() - preedit_pos);
mText.insert(preedit_pos, mPreeditOverwrittenWString);
@@ -2471,19 +2227,24 @@ LLWString LLLineEditor::getConvertedText() const
return text;
}
-namespace LLInitParam
+void LLLineEditor::showContextMenu(S32 x, S32 y)
{
- template<>
- bool ParamCompare<LLLinePrevalidateFunc>::equals(const LLLinePrevalidateFunc &a, const LLLinePrevalidateFunc &b)
- {
- return false;
- }
+ LLContextMenu* menu = static_cast<LLContextMenu*>(mContextMenuHandle.get());
- template<>
- bool ParamCompare<boost::function<void (LLLineEditor *)> >::equals(
- const boost::function<void (LLLineEditor *)> &a,
- const boost::function<void (LLLineEditor *)> &b)
+ if (menu)
{
- return false;
+ 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 78df791334..76d0187712 100644
--- a/indra/llui/lllineeditor.h
+++ b/indra/llui/lllineeditor.h
@@ -11,31 +11,25 @@
* Pre-validation (limit which keys can be used)
* Optional line history so previous entries can be recalled by CTRL UP/DOWN
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -51,26 +45,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,14 +66,16 @@ 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;
- Optional<LLUIImage*> background_image;
+ Optional<LLUIImage*> background_image,
+ background_image_disabled,
+ background_image_focused;
Optional<bool> select_on_focus,
- handle_edit_keys_directly,
+ revert_on_esc,
commit_on_focus_lost,
ignore_tab;
@@ -96,24 +84,21 @@ public:
text_color,
text_readonly_color,
text_tentative_color,
- bg_readonly_color,
- bg_writeable_color,
- bg_focus_color;
-
+ highlight_color,
+ preedit_bg_color;
+
Optional<S32> text_pad_left,
text_pad_right;
- Deprecated is_unicode,
- drop_shadow_visible,
- border_drop_shadow_visible,
- bg_visible;
-
+ Ignored bg_visible;
+
Params();
};
protected:
LLLineEditor(const Params&);
friend class LLUICtrlFactory;
friend class LLFloaterEditUI;
+ void showContextMenu(S32 x, S32 y);
public:
virtual ~LLLineEditor();
@@ -123,6 +108,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();
@@ -163,16 +149,18 @@ public:
virtual void setRect(const LLRect& rect);
virtual BOOL acceptsTextInput() const;
virtual void onCommit();
- virtual BOOL isDirty() const { return mText.getString() != mPrevText; } // Returns TRUE if user changed value at all
- virtual void resetDirty() { mPrevText = mText.getString(); } // Clear dirty state
+ virtual BOOL isDirty() const; // Returns TRUE if user changed value at all
+ virtual void resetDirty(); // Clear dirty state
// assumes UTF8 text
- virtual void setValue(const LLSD& value ) { setText(value.asString()); }
- virtual LLSD getValue() const { return LLSD(getText()); }
+ virtual void setValue(const LLSD& value );
+ virtual LLSD getValue() const;
virtual BOOL setTextArg( const std::string& key, const LLStringExplicit& text );
virtual BOOL setLabelArg( const std::string& key, const LLStringExplicit& text );
void setLabel(const LLStringExplicit &new_label) { mLabel = new_label; }
+ const std::string& getLabel() { return mLabel.getString(); }
+
void setText(const LLStringExplicit &new_text);
const std::string& getText() const { return mText.getString(); }
@@ -187,6 +175,7 @@ public:
// Selects characters 'start' to 'end'.
void setSelection(S32 start, S32 end);
+ virtual void getSelectionRange(S32 *position, S32 *length) const;
void setCommitOnFocusLost( BOOL b ) { mCommitOnFocusLost = b; }
void setRevertOnEsc( BOOL b ) { mRevertOnEsc = b; }
@@ -197,16 +186,12 @@ public:
void setFgColor( const LLColor4& c ) { mFgColor = c; }
void setReadOnlyFgColor( const LLColor4& c ) { mReadOnlyFgColor = c; }
void setTentativeFgColor(const LLColor4& c) { mTentativeFgColor = c; }
- void setWriteableBgColor( const LLColor4& c ) { mWriteableBgColor = c; }
- void setReadOnlyBgColor( const LLColor4& c ) { mReadOnlyBgColor = c; }
- void setFocusBgColor(const LLColor4& c) { mFocusBgColor = c; }
const LLColor4& getFgColor() const { return mFgColor.get(); }
const LLColor4& getReadOnlyFgColor() const { return mReadOnlyFgColor.get(); }
const LLColor4& getTentativeFgColor() const { return mTentativeFgColor.get(); }
- const LLColor4& getWriteableBgColor() const { return mWriteableBgColor.get(); }
- const LLColor4& getReadOnlyBgColor() const { return mReadOnlyBgColor.get(); }
- const LLColor4& getFocusBgColor() const { return mFocusBgColor.get(); }
+
+ const LLFontGL* getFont() const { return mGLFont; }
void setIgnoreArrowKeys(BOOL b) { mIgnoreArrowKeys = b; }
void setIgnoreTab(BOOL b) { mIgnoreTab = b; }
@@ -223,26 +208,18 @@ 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;
void setKeystrokeCallback(callback_t callback, void* user_data);
void setMaxTextLength(S32 max_text_length);
+ // Manipulate left and right padding for text
+ void getTextPadding(S32 *left, S32 *right);
+ void setTextPadding(S32 left, S32 right);
// Prevalidation controls which keystrokes can affect the editor
- void setPrevalidate( 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 prevalidatePrintableNotPipe(const LLWString &str);
- static BOOL prevalidatePrintableNoSpace(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:
@@ -250,7 +227,9 @@ public:
void updateHistory(); // stores current line in history
void setReplaceNewlinesWithSpaces(BOOL replace);
-
+
+ void setContextMenu(LLContextMenu* new_context_menu);
+
private:
// private helper methods
@@ -260,12 +239,14 @@ 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);
S32 handleCommitKey(KEY key, MASK mask);
void updateTextPadding();
+
+ // Draw the background image depending on enabled/focused state.
+ void drawBackground();
//
// private data members
@@ -278,9 +259,9 @@ private:
const segment_lengths_t &preedit_segment_lengths, const standouts_t &preedit_standouts, S32 caret_position);
virtual void markAsPreedit(S32 position, S32 length);
virtual void getPreeditRange(S32 *position, S32 *length) const;
- virtual void getSelectionRange(S32 *position, S32 *length) const;
virtual BOOL getPreeditLocation(S32 query_position, LLCoordGL *coord, LLRect *bounds, LLRect *control) const;
virtual S32 getPreeditFontSize() const;
+ virtual LLWString getPreeditString() const { return getWText(); }
protected:
LLUIString mText; // The string being edited.
@@ -289,20 +270,20 @@ protected:
// line history support:
BOOL mHaveHistory; // flag for enabled line history
- std::vector<std::string> mLineHistory; // line history storage
- U32 mCurrentHistoryLine; // currently browsed history line
+ typedef std::vector<std::string> line_history_t;
+ line_history_t mLineHistory; // line history storage
+ line_history_t::iterator mCurrentHistoryLine; // currently browsed history line
LLViewBorder* mBorder;
const LLFontGL* mGLFont;
- U8 mGLFontStyle;
S32 mMaxLengthBytes; // Max length of the UTF8 string in bytes
S32 mCursorPos; // I-beam is just after the mCursorPos-th character.
S32 mScrollHPos; // Horizontal offset from the start of mText. Used for scrolling.
LLFrameTimer mScrollTimer;
S32 mTextPadLeft; // Used to reserve space before the beginning of the text for children.
S32 mTextPadRight; // Used to reserve space after the end of the text for children.
- S32 mMinHPixels;
- S32 mMaxHPixels;
+ S32 mTextLeftEdge; // Pixels, cached left edge of text based on left padding and width
+ S32 mTextRightEdge; // Pixels, cached right edge of text based on right padding and width
BOOL mCommitOnFocusLost;
BOOL mRevertOnEsc;
@@ -317,7 +298,7 @@ protected:
S32 mLastSelectionStart;
S32 mLastSelectionEnd;
- LLLinePrevalidateFunc mPrevalidateFunc;
+ LLTextValidate::validate_func_t mPrevalidateFunc;
LLFrameTimer mKeystrokeTimer;
LLTimer mTripleClickTimer;
@@ -326,9 +307,8 @@ protected:
LLUIColor mFgColor;
LLUIColor mReadOnlyFgColor;
LLUIColor mTentativeFgColor;
- LLUIColor mWriteableBgColor;
- LLUIColor mReadOnlyBgColor;
- LLUIColor mFocusBgColor;
+ LLUIColor mHighlightColor; // background for selected text
+ LLUIColor mPreeditBgColor; // preedit marker background color
S32 mBorderThickness;
@@ -336,7 +316,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;
@@ -347,9 +326,13 @@ 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> mImage;
+ LLPointer<LLUIImage> mBgImage;
+ LLPointer<LLUIImage> mBgImageDisabled;
+ LLPointer<LLUIImage> mBgImageFocused;
BOOL mReplaceNewlinesWithSpaces; // if false, will replace pasted newlines with paragraph symbol.
@@ -392,22 +375,10 @@ private:
}; // end class LLLineEditor
-#ifdef LL_WINDOWS
-#ifndef INSTANTIATE_GETCHILD_LINEEDITOR
-#pragma warning (disable : 4231)
-extern template LLLineEditor* LLView::getChild<LLLineEditor>( const std::string& name, BOOL recurse, BOOL create_if_missing ) const;
+// Build time optimization, generate once in .cpp file
+#ifndef LLLINEEDITOR_CPP
+extern template class LLLineEditor* LLView::getChild<class LLLineEditor>(
+ const std::string& name, BOOL recurse) const;
#endif
-#endif
-
-namespace LLInitParam
-{
- template<>
- bool ParamCompare<LLLinePrevalidateFunc>::equals(
- const LLLinePrevalidateFunc &a, const LLLinePrevalidateFunc &b);
-
- template<>
- bool ParamCompare<boost::function<void (LLLineEditor *)> >::equals(
- const boost::function<void (LLLineEditor *)> &a, const boost::function<void (LLLineEditor *)> &b);
-}
#endif // LL_LINEEDITOR_
diff --git a/indra/llui/llloadingindicator.cpp b/indra/llui/llloadingindicator.cpp
new file mode 100644
index 0000000000..7b29d92ea0
--- /dev/null
+++ b/indra/llui/llloadingindicator.cpp
@@ -0,0 +1,128 @@
+/**
+ * @file llloadingindicator.cpp
+ * @brief Perpetual loading indicator
+ *
+ * $LicenseInfo:firstyear=2010&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llloadingindicator.h"
+
+// Linden library includes
+#include "llsingleton.h"
+
+// Project includes
+#include "lluictrlfactory.h"
+#include "lluiimage.h"
+
+// registered in llui.cpp to avoid being left out by MS linker
+//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..4e4a224ef6
--- /dev/null
+++ b/indra/llui/llloadingindicator.h
@@ -0,0 +1,87 @@
+/**
+ * @file llloadingindicator.h
+ * @brief Perpetual loading indicator
+ *
+ * $LicenseInfo:firstyear=2010&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_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/lllocalcliprect.cpp b/indra/llui/lllocalcliprect.cpp
new file mode 100644
index 0000000000..6841301219
--- /dev/null
+++ b/indra/llui/lllocalcliprect.cpp
@@ -0,0 +1,110 @@
+/**
+* @file lllocalcliprect.cpp
+*
+* $LicenseInfo:firstyear=2009&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2010, Linden Research, Inc.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation;
+* version 2.1 of the License only.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+* $/LicenseInfo$
+*/
+#include "linden_common.h"
+
+#include "lllocalcliprect.h"
+
+#include "llfontgl.h"
+#include "llui.h"
+
+/*static*/ std::stack<LLRect> LLScreenClipRect::sClipRectStack;
+
+
+LLScreenClipRect::LLScreenClipRect(const LLRect& rect, BOOL enabled)
+: mScissorState(GL_SCISSOR_TEST),
+ mEnabled(enabled)
+{
+ if (mEnabled)
+ {
+ pushClipRect(rect);
+ mScissorState.setEnabled(!sClipRectStack.empty());
+ updateScissorRegion();
+ }
+}
+
+LLScreenClipRect::~LLScreenClipRect()
+{
+ if (mEnabled)
+ {
+ popClipRect();
+ updateScissorRegion();
+ }
+}
+
+//static
+void LLScreenClipRect::pushClipRect(const LLRect& rect)
+{
+ LLRect combined_clip_rect = rect;
+ if (!sClipRectStack.empty())
+ {
+ LLRect top = sClipRectStack.top();
+ combined_clip_rect.intersectWith(top);
+
+ if(combined_clip_rect.isEmpty())
+ {
+ // avoid artifacts where zero area rects show up as lines
+ combined_clip_rect = LLRect::null;
+ }
+ }
+ sClipRectStack.push(combined_clip_rect);
+}
+
+//static
+void LLScreenClipRect::popClipRect()
+{
+ sClipRectStack.pop();
+}
+
+//static
+void LLScreenClipRect::updateScissorRegion()
+{
+ if (sClipRectStack.empty()) return;
+
+ // finish any deferred calls in the old clipping region
+ gGL.flush();
+
+ LLRect rect = sClipRectStack.top();
+ stop_glerror();
+ S32 x,y,w,h;
+ x = llfloor(rect.mLeft * LLUI::sGLScaleFactor.mV[VX]);
+ y = llfloor(rect.mBottom * LLUI::sGLScaleFactor.mV[VY]);
+ w = llmax(0, llceil(rect.getWidth() * LLUI::sGLScaleFactor.mV[VX])) + 1;
+ h = llmax(0, llceil(rect.getHeight() * LLUI::sGLScaleFactor.mV[VY])) + 1;
+ glScissor( x,y,w,h );
+ stop_glerror();
+}
+
+//---------------------------------------------------------------------------
+// LLLocalClipRect
+//---------------------------------------------------------------------------
+LLLocalClipRect::LLLocalClipRect(const LLRect& rect, BOOL enabled /* = TRUE */)
+: LLScreenClipRect(LLRect(rect.mLeft + LLFontGL::sCurOrigin.mX,
+ rect.mTop + LLFontGL::sCurOrigin.mY,
+ rect.mRight + LLFontGL::sCurOrigin.mX,
+ rect.mBottom + LLFontGL::sCurOrigin.mY), enabled)
+{}
+
+LLLocalClipRect::~LLLocalClipRect()
+{}
diff --git a/indra/llui/lllocalcliprect.h b/indra/llui/lllocalcliprect.h
new file mode 100644
index 0000000000..eeeaf2adb6
--- /dev/null
+++ b/indra/llui/lllocalcliprect.h
@@ -0,0 +1,63 @@
+/**
+* @file lllocalcliprect.h
+*
+* $LicenseInfo:firstyear=2009&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2010, Linden Research, Inc.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation;
+* version 2.1 of the License only.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+* $/LicenseInfo$
+*/
+#ifndef LLLOCALCLIPRECT_H
+#define LLLOCALCLIPRECT_H
+
+#include "llgl.h"
+#include "llrect.h" // can't forward declare, it's templated
+#include <stack>
+
+// Clip rendering to a specific rectangle using GL scissor
+// Just create one of these on the stack:
+// {
+// LLLocalClipRect(rect);
+// draw();
+// }
+class LLScreenClipRect
+{
+public:
+ LLScreenClipRect(const LLRect& rect, BOOL enabled = TRUE);
+ virtual ~LLScreenClipRect();
+
+private:
+ static void pushClipRect(const LLRect& rect);
+ static void popClipRect();
+ static void updateScissorRegion();
+
+private:
+ LLGLState mScissorState;
+ BOOL mEnabled;
+
+ static std::stack<LLRect> sClipRectStack;
+};
+
+class LLLocalClipRect : public LLScreenClipRect
+{
+public:
+ LLLocalClipRect(const LLRect& rect, BOOL enabled = TRUE);
+ ~LLLocalClipRect();
+};
+
+#endif
diff --git a/indra/llui/llmenubutton.cpp b/indra/llui/llmenubutton.cpp
new file mode 100644
index 0000000000..3df05f4d3f
--- /dev/null
+++ b/indra/llui/llmenubutton.cpp
@@ -0,0 +1,137 @@
+/**
+ * @file llbutton.cpp
+ * @brief LLButton base class
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llmenubutton.h"
+
+// Linden library includes
+#include "llmenugl.h"
+#include "llstring.h"
+#include "v4color.h"
+
+static LLDefaultChildRegistry::Register<LLMenuButton> r("menu_button");
+
+
+LLMenuButton::Params::Params()
+: menu_filename("menu_filename")
+{
+}
+
+
+LLMenuButton::LLMenuButton(const LLMenuButton::Params& p)
+: LLButton(p),
+ mMenu(NULL),
+ mMenuVisibleLastFrame(false)
+{
+ std::string menu_filename = p.menu_filename;
+
+ if (!menu_filename.empty())
+ {
+ mMenu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>(menu_filename, LLMenuGL::sMenuContainer, LLMenuHolderGL::child_registry_t::instance());
+ if (!mMenu)
+ {
+ llwarns << "Error loading menu_button menu" << llendl;
+ }
+ }
+}
+
+void LLMenuButton::toggleMenu()
+{
+ if(!mMenu)
+ return;
+
+ if (mMenu->getVisible() || mMenuVisibleLastFrame)
+ {
+ mMenu->setVisible(FALSE);
+ }
+ else
+ {
+ LLRect rect = getRect();
+ //mMenu->needsArrange(); //so it recalculates the visible elements
+ LLMenuGL::showPopup(getParent(), mMenu, rect.mLeft, rect.mBottom);
+ }
+}
+
+
+void LLMenuButton::hideMenu()
+{
+ if(!mMenu)
+ return;
+ mMenu->setVisible(FALSE);
+}
+
+
+BOOL LLMenuButton::handleKeyHere(KEY key, MASK mask )
+{
+ if( KEY_RETURN == key && mask == MASK_NONE && !gKeyboard->getKeyRepeated(key))
+ {
+ toggleMenu();
+ return TRUE;
+ }
+
+ if (mMenu && mMenu->getVisible() && key == KEY_ESCAPE && mask == MASK_NONE)
+ {
+ mMenu->setVisible(FALSE);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+BOOL LLMenuButton::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ if (hasTabStop() && !getIsChrome())
+ {
+ setFocus(TRUE);
+ }
+
+ toggleMenu();
+
+ if (getSoundFlags() & MOUSE_DOWN)
+ {
+ make_ui_sound("UISndClick");
+ }
+
+ return TRUE;
+}
+
+void LLMenuButton::draw()
+{
+ //we save this off so next frame when we try to close it by
+ //button click, and it hides menus before we get to it, we know
+ mMenuVisibleLastFrame = mMenu && mMenu->getVisible();
+
+ if (mMenuVisibleLastFrame)
+ {
+ setForcePressedState(true);
+ }
+
+ LLButton::draw();
+
+ setForcePressedState(false);
+}
+
diff --git a/indra/llui/llmenubutton.h b/indra/llui/llmenubutton.h
new file mode 100644
index 0000000000..81ca0e047c
--- /dev/null
+++ b/indra/llui/llmenubutton.h
@@ -0,0 +1,64 @@
+/**
+ * @file llbutton.h
+ * @brief Header for buttons
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLMENUBUTTON_H
+#define LL_LLMENUBUTTON_H
+
+#include "llbutton.h"
+
+class LLMenuGL;
+
+class LLMenuButton
+: public LLButton
+{
+public:
+ struct Params
+ : public LLInitParam::Block<Params, LLButton::Params>
+ {
+ // filename for it's toggleable menu
+ Optional<std::string> menu_filename;
+
+ Params();
+ };
+
+ void toggleMenu();
+ /*virtual*/ void draw();
+ /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleKeyHere(KEY key, MASK mask );
+ void hideMenu();
+ LLMenuGL* getMenu() { return mMenu; }
+
+protected:
+ friend class LLUICtrlFactory;
+ LLMenuButton(const Params&);
+
+private:
+ LLMenuGL* mMenu;
+ bool mMenuVisibleLastFrame;
+};
+
+
+#endif // LL_LLMENUBUTTON_H
diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp
index 4af1c1241b..6d590cf54e 100644
--- a/indra/llui/llmenugl.cpp
+++ b/indra/llui/llmenugl.cpp
@@ -2,31 +2,25 @@
* @file llmenugl.cpp
* @brief LLMenuItemGL base class
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -46,17 +40,19 @@
#include "llmenugl.h"
+#include "llgl.h"
#include "llmath.h"
#include "llrender.h"
#include "llfocusmgr.h"
-#include "llfont.h"
#include "llcoord.h"
#include "llwindow.h"
#include "llcriticaldamp.h"
#include "lluictrlfactory.h"
+#include "llbutton.h"
#include "llfontgl.h"
#include "llresmgr.h"
+#include "lltrans.h"
#include "llui.h"
#include "llstl.h"
@@ -75,10 +71,6 @@ S32 MENU_BAR_WIDTH = 0;
/// Local function declarations, constants, enums, and typedefs
///============================================================================
-const std::string SEPARATOR_NAME("separator");
-const std::string SEPARATOR_LABEL( "-----------" );
-const std::string VERTICAL_SEPARATOR_LABEL( "|" );
-
const S32 LABEL_BOTTOM_PAD_PIXELS = 2;
const U32 LEFT_PAD_PIXELS = 3;
@@ -89,7 +81,6 @@ const U32 RIGHT_PAD_PIXELS = 2;
const U32 RIGHT_WIDTH_PIXELS = 15;
const U32 RIGHT_PLAIN_PIXELS = RIGHT_PAD_PIXELS + RIGHT_WIDTH_PIXELS;
-const U32 ACCEL_PAD_PIXELS = 10;
const U32 PLAIN_PAD_PIXELS = LEFT_PAD_PIXELS + LEFT_WIDTH_PIXELS + RIGHT_PAD_PIXELS + RIGHT_WIDTH_PIXELS;
const U32 BRIEF_PAD_PIXELS = 2;
@@ -98,10 +89,14 @@ const U32 SEPARATOR_HEIGHT_PIXELS = 8;
const S32 TEAROFF_SEPARATOR_HEIGHT_PIXELS = 10;
const S32 MENU_ITEM_PADDING = 4;
-const std::string BOOLEAN_TRUE_PREFIX( "X" );
-const std::string BRANCH_SUFFIX( ">" );
-const std::string ARROW_UP ("^^^^^^^");
-const std::string ARROW_DOWN("vvvvvvv");
+const std::string SEPARATOR_NAME("separator");
+const std::string SEPARATOR_LABEL( "-----------" );
+const std::string VERTICAL_SEPARATOR_LABEL( "|" );
+
+const std::string LLMenuGL::BOOLEAN_TRUE_PREFIX( "\xE2\x9C\x94" ); // U+2714 HEAVY CHECK MARK
+const std::string LLMenuGL::BRANCH_SUFFIX( "\xE2\x96\xB6" ); // U+25B6 BLACK RIGHT-POINTING TRIANGLE
+const std::string LLMenuGL::ARROW_UP ("^^^^^^^");
+const std::string LLMenuGL::ARROW_DOWN("vvvvvvv");
const F32 MAX_MOUSE_SLOPE_SUB_MENU = 0.9f;
@@ -119,27 +114,49 @@ const F32 PIE_SHRINK_TIME = 0.2f; // time of transition between unbounded and bo
const F32 ACTIVATE_HIGHLIGHT_TIME = 0.3f;
-// widget registrars
-struct MenuRegistry : public LLWidgetRegistry<MenuRegistry>
-{};
-
+static MenuRegistry::Register<LLMenuItemGL> register_menu_item("menu_item");
static MenuRegistry::Register<LLMenuItemSeparatorGL> register_separator("menu_item_separator");
static MenuRegistry::Register<LLMenuItemCallGL> register_menu_item_call("menu_item_call");
static MenuRegistry::Register<LLMenuItemCheckGL> register_menu_item_check("menu_item_check");
+// Created programmatically but we need to specify custom colors in xml
+static MenuRegistry::Register<LLMenuItemTearOffGL> register_menu_item_tear_off("menu_item_tear_off");
static MenuRegistry::Register<LLMenuGL> register_menu("menu");
-static LLDefaultWidgetRegistry::Register<LLMenuGL> register_menu_default("menu");
+static LLDefaultChildRegistry::Register<LLMenuGL> register_menu_default("menu");
///============================================================================
/// Class LLMenuItemGL
///============================================================================
+
+LLMenuItemGL::Params::Params()
+: shortcut("shortcut"),
+ jump_key("jump_key", KEY_NONE),
+ use_mac_ctrl("use_mac_ctrl", false),
+ allow_key_repeat("allow_key_repeat", false),
+ rect("rect"),
+ left("left"),
+ top("top"),
+ right("right"),
+ bottom("bottom"),
+ width("width"),
+ height("height"),
+ bottom_delta("bottom_delta"),
+ left_delta("left_delta"),
+ enabled_color("enabled_color"),
+ disabled_color("disabled_color"),
+ highlight_bg_color("highlight_bg_color"),
+ highlight_fg_color("highlight_fg_color")
+{
+ mouse_opaque = true;
+}
+
// Default constructor
LLMenuItemGL::LLMenuItemGL(const LLMenuItemGL::Params& p)
: LLUICtrl(p),
mJumpKey(p.jump_key),
- mAllowKeyRepeat(FALSE),
+ mAllowKeyRepeat(p.allow_key_repeat),
mHighlight( FALSE ),
mGotHover( FALSE ),
mBriefItem( FALSE ),
@@ -181,8 +198,27 @@ LLMenuItemGL::LLMenuItemGL(const LLMenuItemGL::Params& p)
std::string key_str = shortcut.substr(pipe_pos+1);
LLKeyboard::keyFromString(key_str, &mAcceleratorKey);
+
+ LL_DEBUGS("HotKeys") << "Process short cut key: shortcut: " << shortcut
+ << ", key str: " << key_str
+ << ", accelerator mask: " << mAcceleratorMask
+ << ", accelerator key: " << mAcceleratorKey
+ << LL_ENDL;
}
+//virtual
+void LLMenuItemGL::setValue(const LLSD& value)
+{
+ setLabel(value.asString());
+}
+
+//virtual
+LLSD LLMenuItemGL::getValue() const
+{
+ return getLabel();
+}
+
+//virtual
BOOL LLMenuItemGL::handleAcceleratorKey(KEY key, MASK mask)
{
if( getEnabled() && (!gKeyboard->getKeyRepeated(key) || mAllowKeyRepeat) && (key == mAcceleratorKey) && (mask == (mAcceleratorMask & MASK_NORMALKEYS)) )
@@ -200,6 +236,26 @@ BOOL LLMenuItemGL::handleHover(S32 x, S32 y, MASK mask)
return TRUE;
}
+//virtual
+BOOL LLMenuItemGL::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+ return LLUICtrl::handleRightMouseDown(x,y,mask);
+}
+
+//virtual
+BOOL LLMenuItemGL::handleRightMouseUp(S32 x, S32 y, MASK mask)
+{
+ // If this event came from a right-click context menu spawn,
+ // process as a left-click to allow menu items to be hit
+ if (LLMenuHolderGL::sContextMenuSpawnPos.mX != S32_MAX
+ || LLMenuHolderGL::sContextMenuSpawnPos.mY != S32_MAX)
+ {
+ BOOL handled = handleMouseUp(x, y, mask);
+ return handled;
+ }
+ return LLUICtrl::handleRightMouseUp(x,y,mask);
+}
+
// This function checks to see if the accelerator key is already in use;
// if not, it will be added to the list
BOOL LLMenuItemGL::addToAcceleratorList(std::list <LLKeyBinding*> *listp)
@@ -249,47 +305,8 @@ BOOL LLMenuItemGL::addToAcceleratorList(std::list <LLKeyBinding*> *listp)
// the current accelerator key and mask to the provided string.
void LLMenuItemGL::appendAcceleratorString( std::string& st ) const
{
- // break early if this is a silly thing to do.
- if( KEY_NONE == mAcceleratorKey )
- {
- return;
- }
-
- // Append any masks
-#ifdef LL_DARWIN
- // Standard Mac names for modifier keys in menu equivalents
- // We could use the symbol characters, but they only exist in certain fonts.
- if( mAcceleratorMask & MASK_CONTROL )
- {
- if ( mAcceleratorMask & MASK_MAC_CONTROL )
- {
- st.append( "Ctrl-" );
- }
- else
- {
- st.append( "Cmd-" ); // Symbol would be "\xE2\x8C\x98"
- }
- }
- if( mAcceleratorMask & MASK_ALT )
- st.append( "Opt-" ); // Symbol would be "\xE2\x8C\xA5"
- if( mAcceleratorMask & MASK_SHIFT )
- st.append( "Shift-" ); // Symbol would be "\xE2\x8C\xA7"
-#else
- if( mAcceleratorMask & MASK_CONTROL )
- st.append( "Ctrl-" );
- if( mAcceleratorMask & MASK_ALT )
- st.append( "Alt-" );
- if( mAcceleratorMask & MASK_SHIFT )
- st.append( "Shift-" );
-#endif
-
- std::string keystr = LLKeyboard::stringFromKey( mAcceleratorKey );
- if ((mAcceleratorMask & MASK_NORMALKEYS) &&
- (keystr[0] == '-' || keystr[0] == '='))
- {
- st.append( " " );
- }
- st.append( keystr );
+ st = LLKeyboard::stringFromAccelerator( mAcceleratorMask, mAcceleratorKey );
+ LL_DEBUGS("HotKeys") << "appendAcceleratorString: " << st << LL_ENDL;
}
void LLMenuItemGL::setJumpKey(KEY key)
@@ -304,9 +321,20 @@ U32 LLMenuItemGL::getNominalHeight( void ) const
return llround(mFont->getLineHeight()) + MENU_ITEM_PADDING;
}
+//virtual
+void LLMenuItemGL::setBriefItem(BOOL brief)
+{
+ mBriefItem = brief;
+}
+
+//virtual
+BOOL LLMenuItemGL::isBriefItem() const
+{
+ return mBriefItem;
+}
// Get the parent menu for this item
-LLMenuGL* LLMenuItemGL::getMenu()
+LLMenuGL* LLMenuItemGL::getMenu() const
{
return (LLMenuGL*) getParent();
}
@@ -330,7 +358,7 @@ U32 LLMenuItemGL::getNominalWidth( void ) const
if( KEY_NONE != mAcceleratorKey )
{
- width += ACCEL_PAD_PIXELS;
+ width += getMenu()->getShortcutPad();
std::string temp;
appendAcceleratorString( temp );
width += mFont->getWidth( temp );
@@ -350,8 +378,10 @@ void LLMenuItemGL::buildDrawLabel( void )
void LLMenuItemGL::onCommit( void )
{
- // close all open menus by default
- // if parent menu is actually visible (and we are not triggering menu item via accelerator)
+ // Check torn-off status to allow left-arrow keyboard navigation back
+ // to parent menu.
+ // Also, don't hide if item triggered by keyboard shortcut (and hence
+ // parent not visible).
if (!getMenu()->getTornOff()
&& getMenu()->getVisible())
{
@@ -368,6 +398,12 @@ void LLMenuItemGL::onCommit( void )
{
getMenu()->clearHoverItem();
}
+
+ if (mHighlight != highlight)
+ {
+ dirtyRect();
+ }
+
mHighlight = highlight;
}
@@ -444,17 +480,12 @@ void LLMenuItemGL::draw( void )
if (dynamic_cast<LLMenuItemCallGL*>(this))
debug_count++;
gGL.color4fv( mHighlightBackground.get().mV );
+
gl_rect_2d( 0, getRect().getHeight(), getRect().getWidth(), 0 );
}
LLColor4 color;
- LLFontGL::ShadowType shadow_style = LLFontGL::NO_SHADOW;
- if (getEnabled() && !mDrawTextDisabled )
- {
- shadow_style = LLFontGL::DROP_SHADOW_SOFT;
- }
-
if ( getEnabled() && getHighlight() )
{
color = mHighlightForeground.get();
@@ -472,26 +503,26 @@ void LLMenuItemGL::draw( void )
if (mBriefItem)
{
mFont->render( mLabel, 0, BRIEF_PAD_PIXELS / 2, 0, color,
- LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, shadow_style );
+ LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL);
}
else
{
if( !mDrawBoolLabel.empty() )
{
mFont->render( mDrawBoolLabel.getWString(), 0, (F32)LEFT_PAD_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f) + 1.f, color,
- LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, shadow_style, S32_MAX, S32_MAX, NULL, FALSE );
+ LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE );
}
mFont->render( mLabel.getWString(), 0, (F32)LEFT_PLAIN_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f) + 1.f, color,
- LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, shadow_style, S32_MAX, S32_MAX, NULL, FALSE );
+ LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE );
if( !mDrawAccelLabel.empty() )
{
mFont->render( mDrawAccelLabel.getWString(), 0, (F32)getRect().mRight - (F32)RIGHT_PLAIN_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f) + 1.f, color,
- LLFontGL::RIGHT, LLFontGL::BOTTOM, LLFontGL::NORMAL, shadow_style, S32_MAX, S32_MAX, NULL, FALSE );
+ LLFontGL::RIGHT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE );
}
if( !mDrawBranchLabel.empty() )
{
mFont->render( mDrawBranchLabel.getWString(), 0, (F32)getRect().mRight - (F32)RIGHT_PAD_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f) + 1.f, color,
- LLFontGL::RIGHT, LLFontGL::BOTTOM, LLFontGL::NORMAL, shadow_style, S32_MAX, S32_MAX, NULL, FALSE );
+ LLFontGL::RIGHT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE );
}
}
@@ -519,12 +550,13 @@ BOOL LLMenuItemGL::setLabelArg( const std::string& key, const LLStringExplicit&
return TRUE;
}
-void LLMenuItemGL::onVisibilityChange(BOOL new_visibility)
+void LLMenuItemGL::handleVisibilityChange(BOOL new_visibility)
{
if (getMenu())
{
getMenu()->needsArrange();
}
+ LLView::handleVisibilityChange(new_visibility);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -562,12 +594,13 @@ BOOL LLMenuItemSeparatorGL::handleMouseDown(S32 x, S32 y, MASK mask)
LLMenuGL* parent_menu = getMenu();
if (y > getRect().getHeight() / 2)
{
- LLView* prev_menu_item = parent_menu->findPrevSibling(this);
+ // the menu items are in the child list in bottom up order
+ LLView* prev_menu_item = parent_menu->findNextSibling(this);
return prev_menu_item ? prev_menu_item->handleMouseDown(x, prev_menu_item->getRect().getHeight(), mask) : FALSE;
}
else
{
- LLView* next_menu_item = parent_menu->findNextSibling(this);
+ LLView* next_menu_item = parent_menu->findPrevSibling(this);
return next_menu_item ? next_menu_item->handleMouseDown(x, 0, mask) : FALSE;
}
}
@@ -577,12 +610,12 @@ BOOL LLMenuItemSeparatorGL::handleMouseUp(S32 x, S32 y, MASK mask)
LLMenuGL* parent_menu = getMenu();
if (y > getRect().getHeight() / 2)
{
- LLView* prev_menu_item = parent_menu->findPrevSibling(this);
+ LLView* prev_menu_item = parent_menu->findNextSibling(this);
return prev_menu_item ? prev_menu_item->handleMouseUp(x, prev_menu_item->getRect().getHeight(), mask) : FALSE;
}
else
{
- LLView* next_menu_item = parent_menu->findNextSibling(this);
+ LLView* next_menu_item = parent_menu->findPrevSibling(this);
return next_menu_item ? next_menu_item->handleMouseUp(x, 0, mask) : FALSE;
}
}
@@ -626,11 +659,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()
{
@@ -649,7 +709,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)
@@ -730,19 +790,27 @@ LLMenuItemCallGL::LLMenuItemCallGL(const LLMenuItemCallGL::Params& p)
void LLMenuItemCallGL::initFromParams(const Params& p)
{
+ if (p.on_visible.isProvided())
+ {
+ mVisibleSignal.connect(initEnableCallback(p.on_visible));
+ }
if (p.on_enable.isProvided())
{
- initEnableCallback(p.on_enable, mEnableSignal);
+ setEnableCallback(initEnableCallback(p.on_enable));
// Set the enabled control variable (for backwards compatability)
if (p.on_enable.control_name.isProvided() && !p.on_enable.control_name().empty())
{
LLControlVariable* control = findControl(p.on_enable.control_name());
if (control)
+ {
setEnabledControlVariable(control);
+ }
}
}
if (p.on_click.isProvided())
- initCommitCallback(p.on_click, mCommitSignal);
+ {
+ setCommitCallback(initCommitCallback(p.on_click));
+ }
LLUICtrl::initFromParams(p);
}
@@ -763,7 +831,10 @@ void LLMenuItemCallGL::updateEnabled( void )
if (mEnabledControlVariable)
{
if (!enabled)
- mEnabledControlVariable->set(false); // callback overrides control variable; this will call setEnabled()
+ {
+ // callback overrides control variable; this will call setEnabled()
+ mEnabledControlVariable->set(false);
+ }
}
else
{
@@ -772,9 +843,19 @@ void LLMenuItemCallGL::updateEnabled( void )
}
}
+void LLMenuItemCallGL::updateVisible( void )
+{
+ if (mVisibleSignal.num_slots() > 0)
+ {
+ bool visible = mVisibleSignal(this, LLSD());
+ setVisible(visible);
+ }
+}
+
void LLMenuItemCallGL::buildDrawLabel( void )
{
updateEnabled();
+ updateVisible();
LLMenuItemGL::buildDrawLabel();
}
@@ -797,6 +878,9 @@ BOOL LLMenuItemCallGL::handleAcceleratorKey( KEY key, MASK mask )
return FALSE;
}
+// handleRightMouseUp moved into base class LLMenuItemGL so clicks are
+// handled for all menu item types
+
///============================================================================
/// Class LLMenuItemCheckGL
///============================================================================
@@ -809,7 +893,7 @@ void LLMenuItemCheckGL::initFromParams(const Params& p)
{
if (p.on_check.isProvided())
{
- initEnableCallback(p.on_check, mCheckSignal);
+ setCheckCallback(initEnableCallback(p.on_check));
// Set the control name (for backwards compatability)
if (p.on_check.control_name.isProvided() && !p.on_check.control_name().empty())
{
@@ -831,7 +915,7 @@ void LLMenuItemCheckGL::setValue(const LLSD& value)
LLUICtrl::setValue(value);
if(value.asBoolean())
{
- mDrawBoolLabel = BOOLEAN_TRUE_PREFIX;
+ mDrawBoolLabel = LLMenuGL::BOOLEAN_TRUE_PREFIX;
}
else
{
@@ -839,6 +923,15 @@ void LLMenuItemCheckGL::setValue(const LLSD& value)
}
}
+//virtual
+LLSD LLMenuItemCheckGL::getValue() const
+{
+ // Get our boolean value from the view model.
+ // If we don't override this method then the implementation from
+ // LLMenuItemGL will return a string. (EXT-8501)
+ return LLUICtrl::getValue();
+}
+
// called to rebuild the draw label
void LLMenuItemCheckGL::buildDrawLabel( void )
{
@@ -855,7 +948,7 @@ void LLMenuItemCheckGL::buildDrawLabel( void )
}
if(getValue().asBoolean())
{
- mDrawBoolLabel = BOOLEAN_TRUE_PREFIX;
+ mDrawBoolLabel = LLMenuGL::BOOLEAN_TRUE_PREFIX;
}
else
{
@@ -885,24 +978,38 @@ LLMenuItemBranchGL::~LLMenuItemBranchGL()
}
// virtual
-LLView* LLMenuItemBranchGL::getChildView(const std::string& name, BOOL recurse, BOOL create_if_missing) const
+LLView* LLMenuItemBranchGL::getChildView(const std::string& name, BOOL recurse) const
{
LLMenuGL* branch = getBranch();
- if (!branch)
- return LLView::getChildView(name, recurse, create_if_missing);
-
- // richard: this is redundant with parent, remove
- if (branch->getName() == name)
+ if (branch)
{
- return branch;
+ if (branch->getName() == name)
+ {
+ return branch;
+ }
+
+ // Always recurse on branches
+ return branch->getChildView(name, recurse);
}
- // Always recurse on branches
- LLView* child = branch->getChildView(name, recurse, FALSE);
- if (!child)
+
+ return LLView::getChildView(name, recurse);
+}
+
+LLView* LLMenuItemBranchGL::findChildView(const std::string& name, BOOL recurse) const
+{
+ LLMenuGL* branch = getBranch();
+ if (branch)
{
- child = LLView::getChildView(name, recurse, create_if_missing);
+ if (branch->getName() == name)
+ {
+ return branch;
+ }
+
+ // Always recurse on branches
+ return branch->findChildView(name, recurse);
}
- return child;
+
+ return LLView::findChildView(name, recurse);
}
// virtual
@@ -951,7 +1058,7 @@ void LLMenuItemBranchGL::buildDrawLabel( void )
std::string st = mDrawAccelLabel;
appendAcceleratorString( st );
mDrawAccelLabel = st;
- mDrawBranchLabel = BRANCH_SUFFIX;
+ mDrawBranchLabel = LLMenuGL::BRANCH_SUFFIX;
}
void LLMenuItemBranchGL::onCommit( void )
@@ -1061,13 +1168,13 @@ void LLMenuItemBranchGL::updateBranchParent(LLView* parentp)
}
}
-void LLMenuItemBranchGL::onVisibilityChange( BOOL new_visibility )
+void LLMenuItemBranchGL::handleVisibilityChange( BOOL new_visibility )
{
if (new_visibility == FALSE && getBranch() && !getBranch()->getTornOff())
{
getBranch()->setVisible(FALSE);
}
- LLMenuItemGL::onVisibilityChange(new_visibility);
+ LLMenuItemGL::handleVisibilityChange(new_visibility);
}
BOOL LLMenuItemBranchGL::handleKeyHere( KEY key, MASK mask )
@@ -1076,40 +1183,56 @@ BOOL LLMenuItemBranchGL::handleKeyHere( KEY key, MASK mask )
if (!branch)
return LLMenuItemGL::handleKeyHere(key, mask);
- if (getMenu()->getVisible() && branch->getVisible() && key == KEY_LEFT)
+ // an item is highlighted, my menu is open, and I have an active sub menu or we are in
+ // keyboard navigation mode
+ if (getHighlight()
+ && getMenu()->isOpen()
+ && (isActive() || LLMenuGL::getKeyboardMode()))
{
- // switch to keyboard navigation mode
- LLMenuGL::setKeyboardMode(TRUE);
-
- BOOL handled = branch->clearHoverItem();
- if (branch->getTornOff())
- {
- ((LLFloater*)branch->getParent())->setFocus(FALSE);
- }
- if (handled && getMenu()->getTornOff())
+ if (branch->getVisible() && key == KEY_LEFT)
{
- ((LLFloater*)getMenu()->getParent())->setFocus(TRUE);
- }
- return handled;
- }
+ // switch to keyboard navigation mode
+ LLMenuGL::setKeyboardMode(TRUE);
- if (getHighlight() &&
- getMenu()->isOpen() &&
- key == KEY_RIGHT && !branch->getHighlightedItem())
- {
- // switch to keyboard navigation mode
- LLMenuGL::setKeyboardMode(TRUE);
+ BOOL handled = branch->clearHoverItem();
+ if (branch->getTornOff())
+ {
+ ((LLFloater*)branch->getParent())->setFocus(FALSE);
+ }
+ if (handled && getMenu()->getTornOff())
+ {
+ ((LLFloater*)getMenu()->getParent())->setFocus(TRUE);
+ }
+ return handled;
+ }
- LLMenuItemGL* itemp = branch->highlightNextItem(NULL);
- if (itemp)
+ if (key == KEY_RIGHT && !branch->getHighlightedItem())
{
- return TRUE;
+ // switch to keyboard navigation mode
+ LLMenuGL::setKeyboardMode(TRUE);
+
+ LLMenuItemGL* itemp = branch->highlightNextItem(NULL);
+ if (itemp)
+ {
+ return TRUE;
+ }
}
}
-
return LLMenuItemGL::handleKeyHere(key, mask);
}
+//virtual
+BOOL LLMenuItemBranchGL::isActive() const
+{
+ return isOpen() && getBranch() && getBranch()->getHighlightedItem();
+}
+
+//virtual
+BOOL LLMenuItemBranchGL::isOpen() const
+{
+ return getBranch() && getBranch()->isOpen();
+}
+
void LLMenuItemBranchGL::openMenu()
{
LLMenuGL* branch = getBranch();
@@ -1129,40 +1252,45 @@ void LLMenuItemBranchGL::openMenu()
branch->arrange();
- LLRect rect = branch->getRect();
+ LLRect branch_rect = branch->getRect();
// calculate root-view relative position for branch menu
S32 left = getRect().mRight;
S32 top = getRect().mTop - getRect().mBottom;
localPointToOtherView(left, top, &left, &top, branch->getParent());
- rect.setLeftTopAndSize( left, top,
- rect.getWidth(), rect.getHeight() );
+ branch_rect.setLeftTopAndSize( left, top,
+ branch_rect.getWidth(), branch_rect.getHeight() );
if (branch->getCanTearOff())
{
- rect.translate(0, TEAROFF_SEPARATOR_HEIGHT_PIXELS);
+ branch_rect.translate(0, TEAROFF_SEPARATOR_HEIGHT_PIXELS);
}
- branch->setRect( rect );
- S32 x = 0;
- S32 y = 0;
- branch->localPointToOtherView( 0, 0, &x, &y, branch->getParent() );
+ branch->setRect( branch_rect );
+
+ // if branch extends outside of menu region change the direction it opens in
+ S32 x, y;
S32 delta_x = 0;
S32 delta_y = 0;
+ branch->localPointToOtherView( 0, 0, &x, &y, branch->getParent() );
if( y < menu_region_rect.mBottom )
{
- delta_y = menu_region_rect.mBottom - y;
+ // open upwards if menu extends past bottom
+ // adjust by the height of the menu item branch since it is a submenu
+ delta_y = branch_rect.getHeight() - getRect().getHeight();
}
- S32 menu_region_width = menu_region_rect.getWidth();
- if( x - menu_region_rect.mLeft > menu_region_width - rect.getWidth() )
+ if( x + branch_rect.getWidth() > menu_region_rect.mRight )
{
// move sub-menu over to left side
- delta_x = llmax(-x, (-1 * (rect.getWidth() + getRect().getWidth())));
+ delta_x = llmax(-x, ( -(branch_rect.getWidth() + getRect().getWidth())));
}
branch->translate( delta_x, delta_y );
+
branch->setVisible( TRUE );
branch->getParent()->sendChildToFront(branch);
+
+ dirtyRect();
}
}
@@ -1283,20 +1411,26 @@ void LLMenuItemBranchDownGL::openMenu( void )
// set the hover status (called by it's menu)
void LLMenuItemBranchDownGL::setHighlight( BOOL highlight )
{
- if (highlight == getHighlight()) return;
+ if (highlight == getHighlight())
+ return;
//NOTE: Purposely calling all the way to the base to bypass auto-open.
LLMenuItemGL::setHighlight(highlight);
+
+ LLMenuGL* branch = getBranch();
+ if (!branch)
+ return;
+
if( !highlight)
{
- if (getBranch()->getTornOff())
+ if (branch->getTornOff())
{
- ((LLFloater*)getBranch()->getParent())->setFocus(FALSE);
- getBranch()->clearHoverItem();
+ ((LLFloater*)branch->getParent())->setFocus(FALSE);
+ branch->clearHoverItem();
}
else
{
- getBranch()->setVisible( FALSE );
+ branch->setVisible( FALSE );
}
}
}
@@ -1341,7 +1475,7 @@ BOOL LLMenuItemBranchDownGL::handleKeyHere(KEY key, MASK mask)
{
BOOL menu_open = getBranch()->getVisible();
// don't do keyboard navigation of top-level menus unless in keyboard mode, or menu expanded
- if (getHighlight() && getMenu()->getVisible() && (isActive() || LLMenuGL::getKeyboardMode()))
+ if (getHighlight() && getMenu()->isOpen() && (isActive() || LLMenuGL::getKeyboardMode()))
{
if (key == KEY_LEFT)
{
@@ -1414,12 +1548,6 @@ void LLMenuItemBranchDownGL::draw( void )
gl_rect_2d( 0, getRect().getHeight(), getRect().getWidth(), 0 );
}
- LLFontGL::ShadowType shadow_style = LLFontGL::NO_SHADOW;
- if (getEnabled() && !getDrawTextDisabled() )
- {
- shadow_style = LLFontGL::DROP_SHADOW_SOFT;
- }
-
LLColor4 color;
if (getHighlight())
{
@@ -1434,7 +1562,7 @@ void LLMenuItemBranchDownGL::draw( void )
color = mDisabledColor.get();
}
getFont()->render( mLabel.getWString(), 0, (F32)getRect().getWidth() / 2.f, (F32)LABEL_BOTTOM_PAD_PIXELS, color,
- LLFontGL::HCENTER, LLFontGL::BOTTOM, LLFontGL::NORMAL, shadow_style );
+ LLFontGL::HCENTER, LLFontGL::BOTTOM, LLFontGL::NORMAL);
// underline navigation key only when keyboard navigation has been initiated
@@ -1457,6 +1585,7 @@ void LLMenuItemBranchDownGL::draw( void )
setHover(FALSE);
}
+
class LLMenuScrollItem : public LLMenuItemCallGL
{
public:
@@ -1465,10 +1594,18 @@ public:
ARROW_DOWN,
ARROW_UP
};
+ struct ArrowTypes : public LLInitParam::TypeValuesHelper<EArrowType, ArrowTypes>
+ {
+ static void declareValues()
+ {
+ declare("up", ARROW_UP);
+ declare("down", ARROW_DOWN);
+ }
+ };
struct Params : public LLInitParam::Block<Params, LLMenuItemCallGL::Params>
{
- Optional<EArrowType> arrow_type;
+ Optional<EArrowType, ArrowTypes> arrow_type;
Optional<CommitCallbackParam> scroll_callback;
};
@@ -1500,8 +1637,6 @@ LLMenuScrollItem::LLMenuScrollItem(const Params& p)
}
LLButton::Params bparams;
- bparams.label("");
- bparams.label_selected("");
bparams.mouse_opaque(true);
bparams.scale_image(false);
bparams.click_callback(p.scroll_callback);
@@ -1554,8 +1689,11 @@ 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),
+ mPreferredWidth(p.preferred_width),
mKeepFixedSize( p.keep_fixed_size ),
mLabel (p.label),
mLastMouseX(0),
@@ -1571,8 +1709,8 @@ LLMenuGL::LLMenuGL(const LLMenuGL::Params& p)
mSpilloverMenu(NULL),
mJumpKey(p.jump_key),
mCreateJumpKeys(p.create_jump_keys),
- mParentFloaterHandle(p.parent_floater),
- mNeedsArrange(FALSE)
+ mNeedsArrange(FALSE),
+ mShortcutPad(p.shortcut_pad)
{
typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
boost::char_separator<char> sep("_");
@@ -1598,7 +1736,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
@@ -1610,12 +1748,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);
}
@@ -1646,15 +1783,17 @@ bool LLMenuGL::addChild(LLView* view, S32 tab_group)
void LLMenuGL::removeChild( LLView* ctrl)
{
- LLMenuItemGL* itemp = dynamic_cast<LLMenuItemGL*>(ctrl);
- if (itemp)
+ // previously a dynamic_cast with if statement to check validity
+ // unfortunately removeChild is called by ~LLView, and at that point the
+ // object being deleted is no longer a LLMenuItemGL so a dynamic_cast will fail
+ LLMenuItemGL* itemp = static_cast<LLMenuItemGL*>(ctrl);
+
+ item_list_t::iterator found_it = std::find(mItems.begin(), mItems.end(), (itemp));
+ if (found_it != mItems.end())
{
- item_list_t::iterator found_it = std::find(mItems.begin(), mItems.end(), (itemp));
- if (found_it != mItems.end())
- {
- mItems.erase(found_it);
- }
+ mItems.erase(found_it);
}
+
return LLUICtrl::removeChild(ctrl);
}
@@ -1664,12 +1803,6 @@ BOOL LLMenuGL::postBuild()
return LLUICtrl::postBuild();
}
-const widget_registry_t& LLMenuGL::getChildRegistry() const
-{
- return MenuRegistry::instance();
-}
-
-
// are we the childmost active menu and hence our jump keys should be enabled?
// or are we a free-standing torn-off menu (which uses jump keys too)
BOOL LLMenuGL::jumpKeysActive()
@@ -1779,17 +1912,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;
@@ -1832,12 +1969,16 @@ void LLMenuGL::arrange( void )
item_list_t::iterator first_hidden_item_iter = mItems.end();
S32 height_before_first_visible_item = -1;
S32 visible_items_height = 0;
+ U32 scrollable_items_cnt = 0;
if (mHorizontalLayout)
{
item_list_t::iterator item_iter;
for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
{
+ // do first so LLMenuGLItemCall can call on_visible to determine if visible
+ (*item_iter)->buildDrawLabel();
+
if ((*item_iter)->getVisible())
{
if (!getTornOff()
@@ -1879,6 +2020,9 @@ void LLMenuGL::arrange( void )
for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
{
+ // do first so LLMenuGLItemCall can call on_visible to determine if visible
+ (*item_iter)->buildDrawLabel();
+
if ((*item_iter)->getVisible())
{
if (!getTornOff()
@@ -1926,31 +2070,38 @@ void LLMenuGL::arrange( void )
{
height_before_first_visible_item = height - (*item_iter)->getNominalHeight();
first_visible_item_iter = item_iter;
+ scrollable_items_cnt = 0;
}
- if (-1 != height_before_first_visible_item && 0 == visible_items_height && height - height_before_first_visible_item > max_height - spillover_item_height * 2)
+ if (-1 != height_before_first_visible_item && 0 == visible_items_height &&
+ (++scrollable_items_cnt > mMaxScrollableItems ||
+ height - height_before_first_visible_item > max_height - spillover_item_height * 2 ))
{
first_hidden_item_iter = item_iter;
visible_items_height = height - height_before_first_visible_item - (*item_iter)->getNominalHeight();
+ scrollable_items_cnt--;
}
}
}
}
+ if (mPreferredWidth < U32_MAX)
+ width = llmin(mPreferredWidth, max_width);
+
if (mScrollable)
{
S32 max_items_height = max_height - spillover_item_height * 2;
+ if (visible_items_height == 0)
+ visible_items_height = height - height_before_first_visible_item;
+
// Fix mFirstVisibleItem value, if it doesn't allow to display all items, that can fit
- if (visible_items_height < max_items_height)
+ if (visible_items_height < max_items_height && scrollable_items_cnt < mMaxScrollableItems)
{
- if (visible_items_height == 0)
- {
- visible_items_height = height - height_before_first_visible_item;
- }
-
item_list_t::iterator tmp_iter(first_visible_item_iter);
- while (visible_items_height < max_items_height && first_visible_item_iter != mItems.begin())
+ while (visible_items_height < max_items_height &&
+ scrollable_items_cnt < mMaxScrollableItems &&
+ first_visible_item_iter != mItems.begin())
{
if ((*first_visible_item_iter)->getVisible())
{
@@ -1964,6 +2115,7 @@ void LLMenuGL::arrange( void )
{
visible_items_height += (*first_visible_item_iter)->getNominalHeight();
height_before_first_visible_item -= (*first_visible_item_iter)->getNominalHeight();
+ scrollable_items_cnt++;
}
}
@@ -1972,6 +2124,7 @@ void LLMenuGL::arrange( void )
{
visible_items_height -= (*first_visible_item_iter)->getNominalHeight();
height_before_first_visible_item += (*first_visible_item_iter)->getNominalHeight();
+ scrollable_items_cnt--;
first_visible_item_iter = tmp_iter;
}
if (!(*first_visible_item_iter)->getVisible())
@@ -2056,7 +2209,7 @@ void LLMenuGL::arrange( void )
item_list_t::iterator item_iter;
for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
- {
+ {
if ((*item_iter)->getVisible())
{
if (mScrollable)
@@ -2087,7 +2240,6 @@ void LLMenuGL::arrange( void )
}
}
(*item_iter)->setRect( rect );
- (*item_iter)->buildDrawLabel();
}
}
}
@@ -2115,9 +2267,9 @@ void LLMenuGL::createSpilloverBranch()
// technically, you can't tear off spillover menus, but we're passing the handle
// along just to be safe
LLMenuGL::Params p;
+ std::string label = LLTrans::getString("More");
p.name("More");
- p.label("More"); // *TODO: Translate
- p.parent_floater(mParentFloaterHandle);
+ p.label(label);
p.bg_color(mBackgroundColor);
p.bg_visible(true);
p.can_tear_off(false);
@@ -2126,7 +2278,7 @@ void LLMenuGL::createSpilloverBranch()
LLMenuItemBranchGL::Params branch_params;
branch_params.name = "More";
- branch_params.label = "More"; // *TODO: Translate
+ branch_params.label = label;
branch_params.branch = mSpilloverMenu;
branch_params.font.style = "italic";
@@ -2240,8 +2392,8 @@ void LLMenuGL::createJumpKeys()
{
char jump_key = uppercase_word[i];
- if (LLStringOps::isDigit(jump_key) || LLStringOps::isUpper(jump_key) &&
- mJumpKeys.find(jump_key) == mJumpKeys.end())
+ if (LLStringOps::isDigit(jump_key) || (LLStringOps::isUpper(jump_key) &&
+ mJumpKeys.find(jump_key) == mJumpKeys.end()))
{
mJumpKeys.insert(std::pair<KEY, LLMenuItemGL*>(jump_key, (*item_it)));
(*item_it)->setJumpKey(jump_key);
@@ -2331,10 +2483,10 @@ BOOL LLMenuGL::appendMenu( LLMenuGL* menu )
p.label = menu->getLabel();
p.branch = menu;
p.jump_key = menu->getJumpKey();
- p.enabled_color=LLUI::getCachedColorFunctor("MenuItemEnabledColor");
- p.disabled_color=LLUI::getCachedColorFunctor("MenuItemDisabledColor");
- p.highlight_bg_color=LLUI::getCachedColorFunctor("MenuItemHighlightBgColor");
- p.highlight_fg_color=LLUI::getCachedColorFunctor("MenuItemHighlightFgColor");
+ p.enabled_color=LLUIColorTable::instance().getColor("MenuItemEnabledColor");
+ p.disabled_color=LLUIColorTable::instance().getColor("MenuItemDisabledColor");
+ p.highlight_bg_color=LLUIColorTable::instance().getColor("MenuItemHighlightBgColor");
+ p.highlight_fg_color=LLUIColorTable::instance().getColor("MenuItemHighlightFgColor");
LLMenuItemBranchGL* branch = LLUICtrlFactory::create<LLMenuItemBranchGL>(p);
success &= append( branch );
@@ -2698,7 +2850,7 @@ BOOL LLMenuGL::handleHover( S32 x, S32 y, MASK mask )
((LLMenuItemGL*)viewp)->setHighlight(TRUE);
LLMenuGL::setKeyboardMode(FALSE);
}
- mHasSelection = TRUE;
+ mHasSelection = true;
}
}
}
@@ -2731,6 +2883,7 @@ BOOL LLMenuGL::handleScrollWheel( S32 x, S32 y, S32 clicks )
return TRUE;
}
+
void LLMenuGL::draw( void )
{
if (mNeedsArrange)
@@ -2741,7 +2894,7 @@ void LLMenuGL::draw( void )
if (mDropShadowed && !mTornOff)
{
static LLUICachedControl<S32> drop_shadow_floater ("DropShadowFloater", 0);
- static LLUICachedControl<LLColor4> color_drop_shadow ("ColorDropShadow", *(new LLColor4));
+ static LLUIColor color_drop_shadow = LLUIColorTable::instance().getColor("ColorDropShadow");
gl_drop_shadow(0, getRect().getHeight(), getRect().getWidth(), 0,
color_drop_shadow, drop_shadow_floater );
}
@@ -2776,7 +2929,7 @@ void LLMenuGL::setVisible(BOOL visible)
}
else
{
- mHasSelection = FALSE;
+ mHasSelection = true;
mFadeTimer.stop();
}
@@ -2784,9 +2937,9 @@ void LLMenuGL::setVisible(BOOL visible)
}
}
-LLMenuGL* LLMenuGL::getChildMenuByName(const std::string& name, BOOL recurse) const
+LLMenuGL* LLMenuGL::findChildMenuByName(const std::string& name, BOOL recurse) const
{
- LLView* view = getChildView(name, recurse, FALSE);
+ LLView* view = findChildView(name, recurse);
if (view)
{
LLMenuItemBranchGL* branch = dynamic_cast<LLMenuItemBranchGL*>(view);
@@ -2825,65 +2978,66 @@ void hide_top_view( LLView* view )
}
+// x and y are the desired location for the popup, in the spawning_view's
+// coordinate frame, NOT necessarily the mouse location
// static
void LLMenuGL::showPopup(LLView* spawning_view, LLMenuGL* menu, S32 x, S32 y)
{
- const LLRect menu_region_rect = LLMenuGL::sMenuContainer->getMenuRect();
+ const S32 CURSOR_HEIGHT = 22; // Approximate "normal" cursor size
+ const S32 CURSOR_WIDTH = 12;
+
+ // Save click point for detecting cursor moves before mouse-up.
+ // Must be in local coords to compare with mouseUp events.
+ // If the mouse doesn't move, the menu will stay open ala the Mac.
+ // See also LLContextMenu::show()
+ S32 mouse_x, mouse_y;
+
+ // Resetting scrolling position
+ if (menu->isScrollable())
+ {
+ menu->mFirstVisibleItem = NULL;
+ }
+
+ menu->setVisible( TRUE );
+
+ // Fix menu rect if needed.
+ menu->needsArrange();
+ menu->arrangeAndClear();
+
+ LLUI::getMousePositionLocal(menu->getParent(), &mouse_x, &mouse_y);
+ LLMenuHolderGL::sContextMenuSpawnPos.set(mouse_x,mouse_y);
+
+ const LLRect menu_region_rect = LLMenuGL::sMenuContainer->getRect();
const S32 HPAD = 2;
LLRect rect = menu->getRect();
- //LLView* cur_view = spawning_view;
S32 left = x + HPAD;
S32 top = y;
spawning_view->localPointToOtherView(left, top, &left, &top, menu->getParent());
rect.setLeftTopAndSize( left, top,
rect.getWidth(), rect.getHeight() );
+ menu->setRect( rect );
- //rect.setLeftTopAndSize(x + HPAD, y, rect.getWidth(), rect.getHeight());
- menu->setRect( rect );
+ // Adjust context menu to fit onscreen
+ LLRect mouse_rect;
+ const S32 MOUSE_CURSOR_PADDING = 5;
+ mouse_rect.setLeftTopAndSize(mouse_x - MOUSE_CURSOR_PADDING,
+ mouse_y + MOUSE_CURSOR_PADDING,
+ CURSOR_WIDTH + MOUSE_CURSOR_PADDING * 2,
+ CURSOR_HEIGHT + MOUSE_CURSOR_PADDING * 2);
+ menu->translateIntoRectWithExclusion( menu_region_rect, mouse_rect, FALSE );
+ menu->getParent()->sendChildToFront(menu);
- // Resetting scrolling position
- if (menu->isScrollable())
- {
- menu->mFirstVisibleItem = NULL;
- menu->needsArrange();
- }
- menu->arrangeAndClear(); // Fix menu rect if needed.
- rect = menu->getRect();
- S32 bottom;
- left = rect.mLeft;
- bottom = rect.mBottom;
- //menu->getParent()->localPointToScreen( rect.mLeft, rect.mBottom,
- // &left, &bottom );
- S32 delta_x = 0;
- S32 delta_y = 0;
- if( bottom < menu_region_rect.mBottom )
- {
- // At this point, we need to move the context menu to the
- // other side of the mouse.
- //delta_y = menu_region_rect.mBottom - bottom;
- delta_y = (rect.getHeight() + 2 * HPAD);
- }
- if( left > menu_region_rect.mRight - rect.getWidth() )
- {
- // At this point, we need to move the context menu to the
- // other side of the mouse.
- //delta_x = (window_width - rect.getWidth()) - x;
- delta_x = -(rect.getWidth() + 2 * HPAD);
- }
- menu->translate( delta_x, delta_y );
- menu->setVisible( TRUE );
- menu->getParent()->sendChildToFront(menu);
}
///============================================================================
/// Class LLMenuBarGL
///============================================================================
-static LLDefaultWidgetRegistry::Register<LLMenuBarGL> r2("menu_bar");
+static LLDefaultChildRegistry::Register<LLMenuBarGL> r2("menu_bar");
LLMenuBarGL::LLMenuBarGL( const Params& p )
: LLMenuGL(p),
@@ -2984,19 +3138,6 @@ BOOL LLMenuBarGL::handleMouseDown(S32 x, S32 y, MASK mask)
return LLMenuGL::handleMouseDown(x, y, mask);
}
-BOOL LLMenuBarGL::handleRightMouseDown(S32 x, S32 y, MASK mask)
-{
- // clicks on menu bar closes existing menus from other contexts but leave
- // own menu open so that we get toggle behavior
- if (!getHighlightedItem() || !getHighlightedItem()->isActive())
- {
- LLMenuGL::sMenuContainer->hideMenus();
- }
-
- return LLMenuGL::handleMouseDown(x, y, mask);
-}
-
-
void LLMenuBarGL::draw()
{
LLMenuItemGL* itemp = getHighlightedItem();
@@ -3115,10 +3256,10 @@ BOOL LLMenuBarGL::appendMenu( LLMenuGL* menu )
p.label = menu->getLabel();
p.visible = menu->getVisible();
p.branch = menu;
- p.enabled_color=LLUI::getCachedColorFunctor("MenuItemEnabledColor");
- p.disabled_color=LLUI::getCachedColorFunctor("MenuItemDisabledColor");
- p.highlight_bg_color=LLUI::getCachedColorFunctor("MenuItemHighlightBgColor");
- p.highlight_fg_color=LLUI::getCachedColorFunctor("MenuItemHighlightFgColor");
+ p.enabled_color=LLUIColorTable::instance().getColor("MenuItemEnabledColor");
+ p.disabled_color=LLUIColorTable::instance().getColor("MenuItemDisabledColor");
+ p.highlight_bg_color=LLUIColorTable::instance().getColor("MenuItemHighlightBgColor");
+ p.highlight_fg_color=LLUIColorTable::instance().getColor("MenuItemHighlightFgColor");
LLMenuItemBranchDownGL* branch = LLUICtrlFactory::create<LLMenuItemBranchDownGL>(p);
success &= branch->addToAcceleratorList(&mAccelerators);
@@ -3202,11 +3343,11 @@ BOOL LLMenuBarGL::handleHover( S32 x, S32 y, MASK mask )
///============================================================================
/// Class LLMenuHolderGL
///============================================================================
-LLMenuHolderGL::LLMenuHolderGL()
- : LLPanel()
+LLCoordGL LLMenuHolderGL::sContextMenuSpawnPos(S32_MAX, S32_MAX);
+
+LLMenuHolderGL::LLMenuHolderGL(const LLMenuHolderGL::Params& p)
+ : LLPanel(p)
{
- setName("Menu Holder");
- setMouseOpaque(FALSE);
sItemActivationTimer.stop();
mCanHide = TRUE;
}
@@ -3216,7 +3357,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();
@@ -3258,6 +3399,66 @@ BOOL LLMenuHolderGL::handleRightMouseDown( S32 x, S32 y, MASK mask )
return handled;
}
+// This occurs when you mouse-down to spawn a context menu, hold the button
+// down, move off the menu, then mouse-up. We want this to close the menu.
+BOOL LLMenuHolderGL::handleRightMouseUp( S32 x, S32 y, MASK mask )
+{
+ const S32 SLOP = 2;
+ S32 spawn_dx = (x - sContextMenuSpawnPos.mX);
+ S32 spawn_dy = (y - sContextMenuSpawnPos.mY);
+ if (-SLOP <= spawn_dx && spawn_dx <= SLOP
+ && -SLOP <= spawn_dy && spawn_dy <= SLOP)
+ {
+ // we're still inside the slop region from spawning this menu
+ // so interpret the mouse-up as a single-click to show and leave on
+ // screen
+ sContextMenuSpawnPos.set(S32_MAX, S32_MAX);
+ return TRUE;
+ }
+
+ BOOL handled = LLView::childrenHandleRightMouseUp(x, y, mask) != NULL;
+ if (!handled)
+ {
+ // clicked off of menu, hide them all
+ hideMenus();
+ }
+ return handled;
+}
+
+BOOL LLMenuHolderGL::handleKey(KEY key, MASK mask, BOOL called_from_parent)
+{
+ BOOL handled = false;
+ LLMenuGL* const pMenu = dynamic_cast<LLMenuGL*>(getVisibleMenu());
+
+ if (pMenu)
+ {
+ //eat TAB key - EXT-7000
+ if (key == KEY_TAB && mask == MASK_NONE)
+ {
+ return TRUE;
+ }
+
+ //handle ESCAPE and RETURN key
+ handled = LLPanel::handleKey(key, mask, called_from_parent);
+ if (!handled)
+ {
+ if (pMenu->getHighlightedItem())
+ {
+ handled = pMenu->handleKey(key, mask, TRUE);
+ }
+ else
+ {
+ //highlight first enabled one
+ pMenu->highlightNextItem(NULL);
+ handled = true;
+ }
+ }
+ }
+
+ return handled;
+
+}
+
void LLMenuHolderGL::reshape(S32 width, S32 height, BOOL called_from_parent)
{
if (width != getRect().getWidth() || height != getRect().getHeight())
@@ -3267,17 +3468,17 @@ void LLMenuHolderGL::reshape(S32 width, S32 height, BOOL called_from_parent)
LLView::reshape(width, height, called_from_parent);
}
-BOOL LLMenuHolderGL::hasVisibleMenu() const
+LLView* const LLMenuHolderGL::getVisibleMenu() const
{
for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
{
LLView* viewp = *child_it;
- if (viewp->getVisible() && dynamic_cast<LLMenuBarGL*>(viewp) == NULL)
+ if (viewp->getVisible() && dynamic_cast<LLMenuGL*>(viewp) != NULL)
{
- return TRUE;
+ return viewp;
}
}
- return FALSE;
+ return NULL;
}
@@ -3295,8 +3496,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);
}
@@ -3320,9 +3520,9 @@ void LLMenuHolderGL::setActivatedItem(LLMenuItemGL* item)
/// Class LLTearOffMenu
///============================================================================
LLTearOffMenu::LLTearOffMenu(LLMenuGL* menup) :
- LLFloater()
+ LLFloater(LLSD())
{
- static LLUICachedControl<S32> floater_header_size ("UIFloaterHeaderSize", 0);
+ S32 floater_header_size = getHeaderHeight();
setName(menup->getName());
setTitle(menup->getLabel());
@@ -3335,28 +3535,37 @@ LLTearOffMenu::LLTearOffMenu(LLMenuGL* menup) :
LLRect rect;
menup->localRectToOtherView(LLRect(-1, menup->getRect().getHeight(), menup->getRect().getWidth() + 3, 0), &rect, gFloaterView);
// make sure this floater is big enough for menu
- mTargetHeight = (F32)(rect.getHeight() + floater_header_size + 5);
+ mTargetHeight = (F32)(rect.getHeight() + floater_header_size);
reshape(rect.getWidth(), rect.getHeight());
setRect(rect);
// attach menu to floater
- menup->setFollowsAll();
+ menup->setFollows( FOLLOWS_LEFT | FOLLOWS_BOTTOM );
mOldParent = menup->getParent();
addChild(menup);
menup->setVisible(TRUE);
- menup->translate(-menup->getRect().mLeft + 1, -menup->getRect().mBottom + 1);
+ LLRect menu_rect = menup->getRect();
+ menu_rect.setOriginAndSize( 1, 1,
+ menu_rect.getWidth(), menu_rect.getHeight());
+ menup->setRect(menu_rect);
menup->setDropShadowed(FALSE);
mMenu = menup;
// highlight first item (tear off item will be disabled)
mMenu->highlightNextItem(NULL);
+
+ // Can't do this in postBuild() because that is only called for floaters
+ // constructed from XML.
+ mCloseSignal.connect(boost::bind(&LLTearOffMenu::closeTearOff, this));
}
+LLTearOffMenu::~LLTearOffMenu()
+{
+}
void LLTearOffMenu::draw()
{
- static LLUICachedControl<S32> floater_header_size ("UIFloaterHeaderSize", 0);
mMenu->setBackgroundVisible(isBackgroundOpaque());
mMenu->needsArrange();
@@ -3365,12 +3574,6 @@ void LLTearOffMenu::draw()
// animate towards target height
reshape(getRect().getWidth(), llceil(lerp((F32)getRect().getHeight(), mTargetHeight, LLCriticalDamp::getInterpolant(0.05f))));
}
- else
- {
- // when in stasis, remain big enough to hold menu contents
- mTargetHeight = (F32)(mMenu->getRect().getHeight() + floater_header_size + 4);
- reshape(mMenu->getRect().getWidth() + 3, mMenu->getRect().getHeight() + floater_header_size + 5);
- }
LLFloater::draw();
}
@@ -3452,7 +3655,7 @@ LLTearOffMenu* LLTearOffMenu::create(LLMenuGL* menup)
return tearoffp;
}
-void LLTearOffMenu::onClose(bool app_quitting)
+void LLTearOffMenu::closeTearOff()
{
removeChild(mMenu);
mOldParent->addChild(mMenu);
@@ -3462,7 +3665,6 @@ void LLTearOffMenu::onClose(bool app_quitting)
mMenu->setVisible(FALSE);
mMenu->setTornOff(FALSE);
mMenu->setDropShadowed(TRUE);
- destroy();
}
@@ -3480,6 +3682,11 @@ public:
LLContextMenuBranch(const Params&);
+ virtual ~LLContextMenuBranch()
+ {
+ delete mBranch;
+ }
+
// called to rebuild the draw label
virtual void buildDrawLabel( void );
@@ -3531,16 +3738,19 @@ void LLContextMenuBranch::buildDrawLabel( void )
appendAcceleratorString( st );
mDrawAccelLabel = st;
- // No special branch suffix
- mDrawBranchLabel.clear();
+ mDrawBranchLabel = LLMenuGL::BRANCH_SUFFIX;
}
void LLContextMenuBranch::showSubMenu()
{
- S32 center_x;
- S32 center_y;
- localPointToScreen(getRect().getWidth(), getRect().getHeight() , &center_x, &center_y);
- mBranch->show( center_x, center_y, FALSE);
+ LLMenuItemGL* menu_item = mBranch->getParentMenuItem();
+ if (menu_item != NULL && menu_item->getVisible())
+ {
+ S32 center_x;
+ S32 center_y;
+ localPointToScreen(getRect().getWidth(), getRect().getHeight() , &center_x, &center_y);
+ mBranch->show(center_x, center_y);
+ }
}
// onCommit() - do the primary funcationality of the menu item.
@@ -3569,7 +3779,7 @@ void LLContextMenuBranch::setHighlight( BOOL highlight )
// class LLContextMenu
// A context menu
//-----------------------------------------------------------------------------
-static LLDefaultWidgetRegistry::Register<LLContextMenu> context_menu_register("context_menu");
+static LLDefaultChildRegistry::Register<LLContextMenu> context_menu_register("context_menu");
static MenuRegistry::Register<LLContextMenu> context_menu_register2("context_menu");
@@ -3577,7 +3787,6 @@ LLContextMenu::LLContextMenu(const Params& p)
: LLMenuGL(p),
mHoveredAnyItem(FALSE),
mHoverItem(NULL)
-
{
//setBackgroundVisible(TRUE);
}
@@ -3588,8 +3797,15 @@ void LLContextMenu::setVisible(BOOL visible)
hide();
}
-void LLContextMenu::show(S32 x, S32 y,BOOL adjustCursor)
+// Takes cursor position in screen space?
+void LLContextMenu::show(S32 x, S32 y)
{
+ // Save click point for detecting cursor moves before mouse-up.
+ // Must be in local coords to compare with mouseUp events.
+ // If the mouse doesn't move, the menu will stay open ala the Mac.
+ // See also LLMenuGL::showPopup()
+ LLMenuHolderGL::sContextMenuSpawnPos.set(x,y);
+
arrangeAndClear();
S32 width = getRect().getWidth();
@@ -3597,29 +3813,41 @@ void LLContextMenu::show(S32 x, S32 y,BOOL adjustCursor)
const LLRect menu_region_rect = LLMenuGL::sMenuContainer->getMenuRect();
LLView* parent_view = getParent();
- if(getParentMenuItem())
+ // Open upwards if menu extends past bottom
+ if (y - height < menu_region_rect.mBottom)
{
- S32 parent_width = getParentMenuItem()->getRect().getWidth();
-
- if(x + width > menu_region_rect.getWidth())
- x -= parent_width + width;
+ if (getParentMenuItem()) // Adjust if this is a submenu
+ {
+ y += height - getParentMenuItem()->getNominalHeight();
+ }
+ else
+ {
+ y += height;
+ }
+ }
+
+ // Open out to the left if menu extends past right edge
+ if (x + width > menu_region_rect.mRight)
+ {
+ if (getParentMenuItem())
+ {
+ x -= getParentMenuItem()->getRect().getWidth() + width;
+ }
+ else
+ {
+ x -= width;
+ }
}
S32 local_x, local_y;
parent_view->screenPointToLocal(x, y, &local_x, &local_y);
- // HACK: casting away const. Should use setRect or some helper function instead.
- const_cast<LLRect&>(getRect()).setCenterAndSize(local_x + width/2, local_y - height/2, width, height);
+ LLRect rect;
+ rect.setLeftTopAndSize(local_x, local_y, width, height);
+ setRect(rect);
arrange();
-
- if (translateIntoRect(menu_region_rect,FALSE) && adjustCursor)
- {
- LLUI::setCursorPositionLocal(getParent(), getRect().mLeft , getRect().mTop);
- }
-
LLView::setVisible(TRUE);
-
}
void LLContextMenu::hide()
@@ -3679,58 +3907,8 @@ BOOL LLContextMenu::handleHover( S32 x, S32 y, MASK mask )
return handled;
}
-BOOL LLContextMenu::handleMouseDown( S32 x, S32 y, MASK mask )
-{
- BOOL handled = FALSE;
- // The click was somewhere within our rectangle
- LLMenuItemGL *item = getHighlightedItem();
-
- if (item)
- {
- // lie to the item about where the click happened
- // to make sure it's within the item's rectangle
- handled = item->handleMouseDown( 0, 0, mask );
- }
- else
- {
- // call hidemenus to make sure transient selections get cleared
- ((LLMenuHolderGL*)getParent())->hideMenus();
- }
-
- // always handle mouse down as mouse up will close open menus
- return handled;
-}
-BOOL LLContextMenu::handleMouseUp( S32 x, S32 y, MASK mask )
-{
- BOOL handled = FALSE;
-
- // The click was somewhere within our rectangle
- LLMenuItemGL *item = getHighlightedItem();
-
- if (item)
- {
- // lie to the item about where the click happened
- // to make sure it's within the item's rectangle
- if (item->getEnabled())
- {
- handled = item->handleMouseUp( 0, 0, mask );
- hide();
- }
- }
- else
- {
- // call hidemenus to make sure transient selections get cleared
- ((LLMenuHolderGL*)getParent())->hideMenus();
- }
-
- if (!handled)
- {
- // call hidemenus to make sure transient selections get cleared
- sMenuContainer->hideMenus();
- }
+// handleMouseDown and handleMouseUp are handled by LLMenuGL
- return handled;
-}
BOOL LLContextMenu::handleRightMouseDown(S32 x, S32 y, MASK mask)
{
@@ -3766,8 +3944,6 @@ BOOL LLContextMenu::handleRightMouseDown(S32 x, S32 y, MASK mask)
BOOL LLContextMenu::handleRightMouseUp( S32 x, S32 y, MASK mask )
{
- // release mouse capture when right mouse button released, and we're past the shrink time
-
S32 local_x = x - getRect().mLeft;
S32 local_y = y - getRect().mBottom;
@@ -3802,14 +3978,13 @@ BOOL LLContextMenu::appendContextSubMenu(LLContextMenu *menu)
p.name = menu->getName();
p.label = menu->getLabel();
p.branch = menu;
- p.enabled_color=LLUI::getCachedColorFunctor("MenuItemEnabledColor");
- p.disabled_color=LLUI::getCachedColorFunctor("MenuItemDisabledColor");
- p.highlight_bg_color=LLUI::getCachedColorFunctor("MenuItemHighlightBgColor");
- p.highlight_fg_color=LLUI::getCachedColorFunctor("MenuItemHighlightFgColor");
+ p.enabled_color=LLUIColorTable::instance().getColor("MenuItemEnabledColor");
+ p.disabled_color=LLUIColorTable::instance().getColor("MenuItemDisabledColor");
+ p.highlight_bg_color=LLUIColorTable::instance().getColor("MenuItemHighlightBgColor");
+ p.highlight_fg_color=LLUIColorTable::instance().getColor("MenuItemHighlightFgColor");
item = LLUICtrlFactory::create<LLContextMenuBranch>(p);
LLMenuGL::sMenuContainer->addChild(item->getBranch());
- item->setFont( LLFontGL::getFontSansSerifSmall() );
return append( item );
}
diff --git a/indra/llui/llmenugl.h b/indra/llui/llmenugl.h
index 7d889c291c..19b738312e 100644
--- a/indra/llui/llmenugl.h
+++ b/indra/llui/llmenugl.h
@@ -2,31 +2,25 @@
* @file llmenugl.h
* @brief Declaration of the opengl based menu system.
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -61,9 +55,10 @@ public:
{
Optional<std::string> shortcut;
Optional<KEY> jump_key;
- Optional<bool> use_mac_ctrl;
+ Optional<bool> use_mac_ctrl,
+ allow_key_repeat;
- Deprecated rect,
+ Ignored rect,
left,
top,
right,
@@ -79,36 +74,23 @@ public:
highlight_fg_color;
- Params()
- : shortcut("shortcut"),
- jump_key("", KEY_NONE),
- use_mac_ctrl("use_mac_ctrl", false),
- rect("rect"),
- left("left"),
- top("top"),
- right("right"),
- bottom("bottom"),
- width("width"),
- height("height"),
- bottom_delta("bottom_delta"),
- left_delta("left_delta"),
- enabled_color("enabled_color"),
- disabled_color("disabled_color"),
- highlight_bg_color("highlight_bg_color"),
- highlight_fg_color("highlight_fg_color")
- {
- mouse_opaque = true;
- }
+ Params();
};
protected:
LLMenuItemGL(const Params&);
friend class LLUICtrlFactory;
public:
- virtual void setValue(const LLSD& value) { setLabel(value.asString()); }
- /*virtual*/ void onVisibilityChange(BOOL new_visibility);
+ // LLView overrides
+ /*virtual*/ void handleVisibilityChange(BOOL new_visibility);
+ /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleRightMouseUp(S32 x, S32 y, MASK mask);
+
+ // LLUICtrl overrides
+ /*virtual*/ void setValue(const LLSD& value);
+ /*virtual*/ LLSD getValue() const;
- virtual BOOL handleHover(S32 x, S32 y, MASK mask);
virtual BOOL handleAcceleratorKey(KEY key, MASK mask);
LLColor4 getHighlightBgColor() { return mHighlightBackground.get(); }
@@ -124,8 +106,8 @@ public:
virtual U32 getNominalHeight( void ) const;
// Marks item as not needing space for check marks or accelerator keys
- virtual void setBriefItem(BOOL brief) { mBriefItem = brief; }
- virtual BOOL isBriefItem() const { return mBriefItem; }
+ virtual void setBriefItem(BOOL brief);
+ virtual BOOL isBriefItem() const;
virtual BOOL addToAcceleratorList(std::list<LLKeyBinding*> *listp);
void setAllowKeyRepeat(BOOL allow) { mAllowKeyRepeat = allow; }
@@ -137,7 +119,7 @@ public:
virtual BOOL setLabelArg( const std::string& key, const LLStringExplicit& text );
// Get the parent menu for this item
- virtual class LLMenuGL* getMenu();
+ virtual class LLMenuGL* getMenu() const;
// returns the normal width of this control in pixels - this is
// used for calculating the widest item, as well as for horizontal
@@ -189,9 +171,7 @@ protected:
// This function appends the character string representation of
// the current accelerator key and mask to the provided string.
void appendAcceleratorString( std::string& st ) const;
-
- void initMenuEnableCallback(const EnableCallbackParam& cb, enable_signal_t& sig);
-
+
protected:
KEY mAcceleratorKey;
MASK mAcceleratorMask;
@@ -263,15 +243,18 @@ public:
{
Optional<EnableCallbackParam > on_enable;
Optional<CommitCallbackParam > on_click;
+ Optional<EnableCallbackParam > on_visible;
Params()
: on_enable("on_enable"),
- on_click("on_click")
+ on_click("on_click"),
+ on_visible("on_visible")
{}
};
protected:
LLMenuItemCallGL(const Params&);
friend class LLUICtrlFactory;
void updateEnabled( void );
+ void updateVisible( void );
public:
void initFromParams(const Params& p);
@@ -286,18 +269,19 @@ public:
//virtual void draw();
- boost::signals::connection setClickCallback( const commit_signal_t::slot_type& cb )
+ boost::signals2::connection setClickCallback( const commit_signal_t::slot_type& cb )
{
return setCommitCallback(cb);
}
- boost::signals::connection setEnableCallback( const enable_signal_t::slot_type& cb )
+ boost::signals2::connection setEnableCallback( const enable_signal_t::slot_type& cb )
{
return mEnableSignal.connect(cb);
}
-
+
private:
enable_signal_t mEnableSignal;
+ enable_signal_t mVisibleSignal;
};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -307,7 +291,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
@@ -332,11 +316,12 @@ public:
virtual void onCommit( void );
virtual void setValue(const LLSD& value);
+ virtual LLSD getValue() const;
// called to rebuild the draw label
virtual void buildDrawLabel( void );
- boost::signals::connection setCheckCallback( const enable_signal_t::slot_type& cb )
+ boost::signals2::connection setCheckCallback( const enable_signal_t::slot_type& cb )
{
return mCheckSignal.connect(cb);
}
@@ -356,13 +341,17 @@ private:
// it in the appendMenu() method.
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// child widget registry
+struct MenuRegistry : public LLChildRegistry<MenuRegistry>
+{};
+
+
class LLMenuGL
: public LLUICtrl
{
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,
@@ -371,16 +360,24 @@ public:
create_jump_keys,
keep_fixed_size,
scrollable;
+ Optional<U32> max_scrollable_items;
+ Optional<U32> preferred_width;
Optional<LLUIColor> bg_color;
+ Optional<S32> shortcut_pad;
Params()
- : jump_key("", KEY_NONE),
+ : jump_key("jump_key", KEY_NONE),
+ horizontal_layout("horizontal_layout"),
can_tear_off("tear_off", false),
drop_shadow("drop_shadow", true),
bg_visible("bg_visible", true),
create_jump_keys("create_jump_keys", false),
- bg_color("bg_color", LLUI::getCachedColorFunctor( "MenuDefaultBgColor" )),
- scrollable("scrollable", false)
+ bg_color("bg_color", LLUIColorTable::instance().getColor( "MenuDefaultBgColor" )),
+ scrollable("scrollable", false),
+ max_scrollable_items("max_scrollable_items", U32_MAX),
+ preferred_width("preferred_width", U32_MAX),
+ shortcut_pad("shortcut_pad")
+
{
addSynonym(bg_visible, "opaque");
addSynonym(bg_color, "color");
@@ -388,8 +385,18 @@ public:
name = "menu";
}
};
+
+ // my valid children are contained in MenuRegistry
+ typedef MenuRegistry child_registry_t;
+
void initFromParams(const Params&);
+ // textual artwork which menugl-imitators may want to match
+ static const std::string BOOLEAN_TRUE_PREFIX;
+ static const std::string BRANCH_SUFFIX;
+ static const std::string ARROW_UP;
+ static const std::string ARROW_DOWN;
+
protected:
LLMenuGL(const LLMenuGL::Params& p);
friend class LLUICtrlFactory;
@@ -410,11 +417,10 @@ public:
/*virtual*/ bool addChild(LLView* view, S32 tab_group = 0);
/*virtual*/ void removeChild( LLView* ctrl);
/*virtual*/ BOOL postBuild();
- /*virtual*/ const widget_registry_t& getChildRegistry() const;
virtual BOOL handleAcceleratorKey(KEY key, MASK mask);
- LLMenuGL* getChildMenuByName(const std::string& name, BOOL recurse) const;
+ LLMenuGL* findChildMenuByName(const std::string& name, BOOL recurse) const;
BOOL clearHoverItem();
@@ -426,7 +432,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();
@@ -475,10 +481,7 @@ public:
void buildDrawLabels();
void createJumpKeys();
- // Show popup in global screen space based on last mouse location.
- static void showPopup(LLMenuGL* menu);
-
- // Show popup at a specific location.
+ // Show popup at a specific location, in the spawn_view's coordinate frame
static void showPopup(LLView* spawning_view, LLMenuGL* menu, S32 x, S32 y);
// Whether to drop shadow menu bar
@@ -498,6 +501,8 @@ public:
static void setKeyboardMode(BOOL mode) { sKeyboardMode = mode; }
static BOOL getKeyboardMode() { return sKeyboardMode; }
+ S32 getShortcutPad() { return mShortcutPad; }
+
void scrollItemsUp();
void scrollItemsDown();
BOOL isScrollable() const { return mScrollable; }
@@ -525,6 +530,8 @@ protected:
S32 mLastMouseY;
S32 mMouseVelX;
S32 mMouseVelY;
+ U32 mMaxScrollableItems;
+ U32 mPreferredWidth;
BOOL mHorizontalLayout;
BOOL mScrollable;
BOOL mKeepFixedSize;
@@ -541,16 +548,16 @@ 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;
}; // end class LLMenuGL
@@ -597,16 +604,16 @@ public:
virtual BOOL handleKeyHere(KEY key, MASK mask);
- virtual BOOL isActive() const { return isOpen() && getBranch() && getBranch()->getHighlightedItem(); }
+ virtual BOOL isActive() const;
- virtual BOOL isOpen() const { return getBranch() && getBranch()->isOpen(); }
+ virtual BOOL isOpen() const;
LLMenuGL* getBranch() const { return (LLMenuGL*)mBranchHandle.get(); }
virtual void updateBranchParent( LLView* parentp );
// LLView Functionality
- virtual void onVisibilityChange( BOOL curVisibilityIn );
+ virtual void handleVisibilityChange( BOOL curVisibilityIn );
virtual void draw();
@@ -614,7 +621,8 @@ public:
virtual void openMenu();
- virtual LLView* getChildView(const std::string& name, BOOL recurse = TRUE, BOOL create_if_missing = TRUE) const;
+ virtual LLView* getChildView(const std::string& name, BOOL recurse = TRUE) const;
+ virtual LLView* findChildView(const std::string& name, BOOL recurse = TRUE) const;
private:
LLHandle<LLView> mBranchHandle;
@@ -651,16 +659,12 @@ public:
virtual void draw ();
- virtual void show (S32 x, S32 y, BOOL adjustCursor = TRUE);
+ virtual void show (S32 x, S32 y);
virtual void hide ();
-
-
virtual BOOL handleHover ( S32 x, S32 y, MASK mask );
- virtual BOOL handleMouseDown ( S32 x, S32 y, MASK mask );
virtual BOOL handleRightMouseDown( S32 x, S32 y, MASK mask );
virtual BOOL handleRightMouseUp ( S32 x, S32 y, MASK mask );
- virtual BOOL handleMouseUp ( S32 x, S32 y, MASK mask );
virtual bool addChild (LLView* view, S32 tab_group = 0);
@@ -700,7 +704,6 @@ public:
/*virtual*/ BOOL handleKeyHere(KEY key, MASK mask);
/*virtual*/ BOOL handleJumpKey(KEY key);
/*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask);
- /*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask);
/*virtual*/ void draw();
/*virtual*/ BOOL jumpKeysActive();
@@ -737,7 +740,9 @@ private:
class LLMenuHolderGL : public LLPanel
{
public:
- LLMenuHolderGL();
+ struct Params : public LLInitParam::Block<Params, LLPanel::Params>
+ {};
+ LLMenuHolderGL(const Params& p);
virtual ~LLMenuHolderGL() {}
virtual BOOL hideMenus();
@@ -749,11 +754,20 @@ public:
virtual BOOL handleMouseDown( S32 x, S32 y, MASK mask );
virtual BOOL handleRightMouseDown( S32 x, S32 y, MASK mask );
+ // Close context menus on right mouse up not handled by menus.
+ /*virtual*/ BOOL handleRightMouseUp( S32 x, S32 y, MASK mask );
+
+ virtual BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent);
virtual const LLRect getMenuRect() const { return getLocalRect(); }
- virtual BOOL hasVisibleMenu() const;
+ LLView*const getVisibleMenu() const;
+ virtual BOOL hasVisibleMenu() const {return getVisibleMenu() != NULL;}
static void setActivatedItem(LLMenuItemGL* item);
+ // Need to detect if mouse-up after context menu spawn has moved.
+ // If not, need to keep the menu up.
+ static LLCoordGL sContextMenuSpawnPos;
+
private:
static LLHandle<LLView> sItemLastSelectedHandle;
static LLFrameTimer sItemActivationTimer;
@@ -771,8 +785,8 @@ class LLTearOffMenu : public LLFloater
{
public:
static LLTearOffMenu* create(LLMenuGL* menup);
- virtual ~LLTearOffMenu() {}
- virtual void onClose(bool app_quitting);
+ virtual ~LLTearOffMenu();
+
virtual void draw(void);
virtual void onFocusReceived();
virtual void onFocusLost();
@@ -782,7 +796,9 @@ public:
private:
LLTearOffMenu(LLMenuGL* menup);
-
+
+ void closeTearOff();
+
LLView* mOldParent;
LLMenuGL* mMenu;
F32 mTargetHeight;
@@ -799,7 +815,6 @@ class LLMenuItemTearOffGL : public LLMenuItemGL
public:
struct Params : public LLInitParam::Block<Params, LLMenuItemGL::Params>
{
- Optional<LLHandle<LLFloater> > parent_floater_handle;
Params()
{
name = "tear off";
@@ -808,13 +823,12 @@ public:
};
LLMenuItemTearOffGL( const Params& );
-
+
virtual void onCommit(void);
virtual void draw(void);
virtual U32 getNominalHeight() const;
-private:
- LLHandle<LLFloater> mParentHandle;
+ LLFloater* getParentFloater();
};
@@ -834,7 +848,7 @@ private:
// *TODO: Eliminate
// For backwards compatability only; generally just use boost::bind
-class view_listener_t : public boost::signals::trackable
+class view_listener_t : public boost::signals2::trackable
{
public:
virtual bool handleEvent(const LLSD& userdata) = 0;
diff --git a/indra/llui/llmodaldialog.cpp b/indra/llui/llmodaldialog.cpp
index 8779eee28d..8c2be44904 100644
--- a/indra/llui/llmodaldialog.cpp
+++ b/indra/llui/llmodaldialog.cpp
@@ -2,31 +2,25 @@
* @file llmodaldialog.cpp
* @brief LLModalDialog base class
*
- * $LicenseInfo:firstyear=2002&license=viewergpl$
- *
- * Copyright (c) 2002-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -44,12 +38,10 @@
// static
std::list<LLModalDialog*> LLModalDialog::sModalStack;
-LLModalDialog::LLModalDialog( const std::string& title, S32 width, S32 height, BOOL modal )
- : LLFloater(),
+LLModalDialog::LLModalDialog( const LLSD& key, BOOL modal )
+ : LLFloater(key),
mModal( modal )
{
- setRect(LLRect( 0, height, width, 0 ));
- setTitle(title);
if (modal)
{
setCanMinimize(FALSE);
@@ -59,6 +51,7 @@ LLModalDialog::LLModalDialog( const std::string& title, S32 width, S32 height, B
setBackgroundVisible(TRUE);
setBackgroundOpaque(TRUE);
centerOnScreen(); // default position
+ mCloseSignal.connect(boost::bind(&LLModalDialog::stopModal, this));
}
LLModalDialog::~LLModalDialog()
@@ -68,6 +61,18 @@ LLModalDialog::~LLModalDialog()
{
gFocusMgr.unlockFocus();
}
+
+ std::list<LLModalDialog*>::iterator iter = std::find(sModalStack.begin(), sModalStack.end(), this);
+ if (iter != sModalStack.end())
+ {
+ llerrs << "Attempt to delete dialog while still in sModalStack!" << llendl;
+ }
+}
+
+// virtual
+BOOL LLModalDialog::postBuild()
+{
+ return LLFloater::postBuild();
}
// virtual
@@ -86,7 +91,8 @@ void LLModalDialog::reshape(S32 width, S32 height, BOOL called_from_parent)
centerOnScreen();
}
-void LLModalDialog::startModal()
+// virtual
+void LLModalDialog::onOpen(const LLSD& key)
{
if (mModal)
{
@@ -99,13 +105,11 @@ void LLModalDialog::startModal()
// 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 );
}
-
- setVisible( TRUE );
}
void LLModalDialog::stopModal()
@@ -143,7 +147,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
@@ -235,16 +239,10 @@ BOOL LLModalDialog::handleKeyHere(KEY key, MASK mask )
}
}
-void LLModalDialog::onClose(bool app_quitting)
-{
- stopModal();
- LLFloater::onClose(app_quitting);
-}
-
// virtual
void LLModalDialog::draw()
{
- static LLUICachedControl<LLColor4> shadow_color ("ColorDropShadow", *(new LLColor4));
+ static LLUIColor shadow_color = LLUIColorTable::instance().getColor("ColorDropShadow");
static LLUICachedControl<S32> shadow_lines ("DropShadowFloater", 0);
gl_drop_shadow( 0, getRect().getHeight(), getRect().getWidth(), 0,
@@ -273,10 +271,7 @@ void LLModalDialog::onAppFocusLost()
gFocusMgr.setMouseCapture( NULL );
}
- if( gFocusMgr.childHasKeyboardFocus( instance ) )
- {
- gFocusMgr.setKeyboardFocus( NULL );
- }
+ instance->setFocus(FALSE);
}
}
@@ -290,11 +285,22 @@ 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();
}
}
-
-
+void LLModalDialog::shutdownModals()
+{
+ // This method is only for use during app shutdown. ~LLModalDialog()
+ // checks sModalStack, and if the dialog instance is still there, it
+ // crumps with "Attempt to delete dialog while still in sModalStack!" But
+ // at app shutdown, all bets are off. If the user asks to shut down the
+ // app, we shouldn't have to care WHAT's open. Put differently, if a modal
+ // dialog is so crucial that we can't let the user terminate until s/he
+ // addresses it, we should reject a termination request. The current state
+ // of affairs is that we accept it, but then produce an llerrs popup that
+ // simply makes our software look unreliable.
+ sModalStack.clear();
+}
diff --git a/indra/llui/llmodaldialog.h b/indra/llui/llmodaldialog.h
index dad92ab82a..4e09d11d77 100644
--- a/indra/llui/llmodaldialog.h
+++ b/indra/llui/llmodaldialog.h
@@ -2,31 +2,25 @@
* @file llmodaldialog.h
* @brief LLModalDialog base class
*
- * $LicenseInfo:firstyear=2002&license=viewergpl$
- *
- * Copyright (c) 2002-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -45,16 +39,16 @@ class LLModalDialog;
class LLModalDialog : public LLFloater
{
public:
- LLModalDialog( const std::string& title, S32 width, S32 height, BOOL modal = true );
+ LLModalDialog( const LLSD& key, BOOL modal = true );
/*virtual*/ ~LLModalDialog();
-
+
+ /*virtual*/ BOOL postBuild();
+
/*virtual*/ void openFloater(const LLSD& key = LLSD());
+ /*virtual*/ void onOpen(const LLSD& key);
/*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
- /*virtual*/ void startModal();
- /*virtual*/ void stopModal();
-
/*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask);
/*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask);
/*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask);
@@ -63,22 +57,23 @@ public:
/*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask);
/*virtual*/ BOOL handleKeyHere(KEY key, MASK mask );
- /*virtual*/ void onClose(bool app_quitting);
-
/*virtual*/ void setVisible(BOOL visible);
/*virtual*/ void draw();
- BOOL isModal() const { return mModal; }
+ BOOL isModal() const { return mModal; }
+ void stopModal();
static void onAppFocusLost();
static void onAppFocusGained();
static S32 activeCount() { return sModalStack.size(); }
-
+ static void shutdownModals();
+
protected:
void centerOnScreen();
private:
+
LLFrameTimer mVisibleTime;
const BOOL mModal;
diff --git a/indra/llui/llmultifloater.cpp b/indra/llui/llmultifloater.cpp
index c0fe7ff32d..f3a48835b1 100644
--- a/indra/llui/llmultifloater.cpp
+++ b/indra/llui/llmultifloater.cpp
@@ -2,31 +2,25 @@
* @file llmultifloater.cpp
* @brief LLFloater that hosts other floaters
*
- * $LicenseInfo:firstyear=2002&license=viewergpl$
- *
- * Copyright (c) 2002-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -42,8 +36,8 @@
// LLMultiFloater
//
-LLMultiFloater::LLMultiFloater(const LLFloater::Params& params)
- : LLFloater(),
+LLMultiFloater::LLMultiFloater(const LLSD& key, const LLFloater::Params& params)
+ : LLFloater(key),
mTabContainer(NULL),
mTabPos(LLTabContainer::TOP),
mAutoResize(TRUE),
@@ -54,7 +48,8 @@ LLMultiFloater::LLMultiFloater(const LLFloater::Params& params)
void LLMultiFloater::buildTabContainer()
{
- static LLUICachedControl<S32> floater_header_size ("UIFloaterHeaderSize", 0);
+ const LLFloater::Params& default_params = LLFloater::getDefaultParams();
+ S32 floater_header_size = default_params.header_height;
LLTabContainer::Params p;
p.name(std::string("Preview Tabs"));
@@ -74,20 +69,12 @@ void LLMultiFloater::buildTabContainer()
void LLMultiFloater::onOpen(const LLSD& key)
{
- if (mTabContainer->getTabCount() <= 0)
- {
- // for now, don't allow multifloaters
- // without any child floaters
- closeFloater();
- }
-}
-
-void LLMultiFloater::onClose(bool app_quitting)
-{
- if(closeAllFloaters() == TRUE)
- {
- LLFloater::onClose(app_quitting);
- }//else not all tabs could be closed...
+// if (mTabContainer->getTabCount() <= 0)
+// {
+// // for now, don't allow multifloaters
+// // without any child floaters
+// closeFloater();
+// }
}
void LLMultiFloater::draw()
@@ -99,14 +86,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();
}
}
@@ -124,7 +103,8 @@ BOOL LLMultiFloater::closeAllFloaters()
//Tab did not actually close, possibly due to a pending Save Confirmation dialog..
//so try and close the next one in the list...
tabToClose++;
- }else
+ }
+ else
{
//Tab closed ok.
lastTabCount = mTabContainer->getTabCount();
@@ -138,7 +118,8 @@ BOOL LLMultiFloater::closeAllFloaters()
void LLMultiFloater::growToFit(S32 content_width, S32 content_height)
{
static LLUICachedControl<S32> tabcntr_close_btn_size ("UITabCntrCloseBtnSize", 0);
- static LLUICachedControl<S32> floater_header_size ("UIFloaterHeaderSize", 0);
+ const LLFloater::Params& default_params = LLFloater::getDefaultParams();
+ S32 floater_header_size = default_params.header_height;
S32 tabcntr_header_height = LLPANEL_BORDER_WIDTH + tabcntr_close_btn_size;
S32 new_width = llmax(getRect().getWidth(), content_width + LLPANEL_BORDER_WIDTH * 2);
S32 new_height = llmax(getRect().getHeight(), content_height + floater_header_size + tabcntr_header_height);
@@ -246,8 +227,21 @@ void LLMultiFloater::addFloater(LLFloater* floaterp, BOOL select_added_floater,
{
floaterp->setVisible(FALSE);
}
+
+ // Tabs sometimes overlap resize handle
+ moveResizeHandlesToFront();
}
+void LLMultiFloater::updateFloaterTitle(LLFloater* floaterp)
+{
+ S32 index = mTabContainer->getIndexForPanel(floaterp);
+ if (index != -1)
+ {
+ mTabContainer->setPanelTitle(index, floaterp->getShortTitle());
+ }
+}
+
+
/**
BOOL selectFloater(LLFloater* floaterp)
@@ -275,6 +269,7 @@ void LLMultiFloater::selectPrevFloater()
void LLMultiFloater::showFloater(LLFloater* floaterp, LLTabContainer::eInsertionPoint insertion_point)
{
+ if(!floaterp) return;
// we won't select a panel that already is selected
// it is hard to do this internally to tab container
// as tab selection is handled via index and the tab at a given
@@ -288,7 +283,7 @@ void LLMultiFloater::showFloater(LLFloater* floaterp, LLTabContainer::eInsertion
void LLMultiFloater::removeFloater(LLFloater* floaterp)
{
- if ( floaterp->getHost() != this )
+ if (!floaterp || floaterp->getHost() != this )
return;
floater_data_map_t::iterator found_data_it = mFloaterDataMap.find(floaterp->getHandle());
@@ -354,13 +349,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;
}
@@ -435,6 +437,7 @@ void LLMultiFloater::onTabSelected()
void LLMultiFloater::setCanResize(BOOL can_resize)
{
LLFloater::setCanResize(can_resize);
+ if (!mTabContainer) return;
if (isResizable() && mTabContainer->getTabPosition() == LLTabContainer::BOTTOM)
{
mTabContainer->setRightTabBtnOffset(RESIZE_HANDLE_WIDTH);
@@ -447,6 +450,8 @@ void LLMultiFloater::setCanResize(BOOL can_resize)
BOOL LLMultiFloater::postBuild()
{
+ mCloseSignal.connect(boost::bind(&LLMultiFloater::closeAllFloaters, this));
+
// remember any original xml minimum size
getResizeLimits(&mOrigMinWidth, &mOrigMinHeight);
@@ -455,20 +460,17 @@ BOOL LLMultiFloater::postBuild()
return TRUE;
}
- requires<LLTabContainer>("Preview Tabs");
- if (checkRequirements())
- {
- mTabContainer = getChild<LLTabContainer>("Preview Tabs");
- return TRUE;
- }
-
- return FALSE;
+ mTabContainer = getChild<LLTabContainer>("Preview Tabs");
+
+ setCanResize(mResizable);
+ return TRUE;
}
void LLMultiFloater::updateResizeLimits()
{
static LLUICachedControl<S32> tabcntr_close_btn_size ("UITabCntrCloseBtnSize", 0);
- static LLUICachedControl<S32> floater_header_size ("UIFloaterHeaderSize", 0);
+ const LLFloater::Params& default_params = LLFloater::getDefaultParams();
+ S32 floater_header_size = default_params.header_height;
S32 tabcntr_header_height = LLPANEL_BORDER_WIDTH + tabcntr_close_btn_size;
// initialize minimum size constraint to the original xml values.
S32 new_min_width = mOrigMinWidth;
diff --git a/indra/llui/llmultifloater.h b/indra/llui/llmultifloater.h
index ea8a9841e3..9fa917eca1 100644
--- a/indra/llui/llmultifloater.h
+++ b/indra/llui/llmultifloater.h
@@ -2,31 +2,25 @@
* @file llmultifloater.h
* @brief LLFloater that hosts other floaters
*
- * $LicenseInfo:firstyear=2002&license=viewergpl$
- *
- * Copyright (c) 2002-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -44,14 +38,13 @@
class LLMultiFloater : public LLFloater
{
public:
- LLMultiFloater(const LLFloater::Params& params = LLFloater::Params());
+ LLMultiFloater(const LLSD& key, const Params& params = getDefaultParams());
virtual ~LLMultiFloater() {};
void buildTabContainer();
virtual BOOL postBuild();
/*virtual*/ void onOpen(const LLSD& key);
- /*virtual*/ void onClose(bool app_quitting);
/*virtual*/ void draw();
/*virtual*/ void setVisible(BOOL visible);
/*virtual*/ BOOL handleKeyHere(KEY key, MASK mask);
@@ -81,6 +74,7 @@ public:
void onTabSelected();
virtual void updateResizeLimits();
+ virtual void updateFloaterTitle(LLFloater* floaterp);
protected:
struct LLFloaterData
diff --git a/indra/llui/llmultislider.cpp b/indra/llui/llmultislider.cpp
index 099a79278a..1f6fa12969 100644
--- a/indra/llui/llmultislider.cpp
+++ b/indra/llui/llmultislider.cpp
@@ -2,31 +2,25 @@
* @file llmultisldr.cpp
* @brief LLMultiSlider base class
*
- * $LicenseInfo:firstyear=2007&license=viewergpl$
- *
- * Copyright (c) 2007-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -40,17 +34,22 @@
#include "llfocusmgr.h"
#include "llkeyboard.h" // for the MASK constants
#include "llcontrol.h"
-#include "llimagegl.h"
#include "lluictrlfactory.h"
#include <sstream>
-static LLDefaultWidgetRegistry::Register<LLMultiSlider> r("multi_slider_bar");
+static LLDefaultChildRegistry::Register<LLMultiSlider> r("multi_slider_bar");
const F32 FLOAT_THRESHOLD = 0.00001f;
S32 LLMultiSlider::mNameCounter = 0;
+LLMultiSlider::SliderParams::SliderParams()
+: name("name"),
+ value("value", 0.f)
+{
+}
+
LLMultiSlider::Params::Params()
: max_sliders("max_sliders", 1),
allow_overlap("allow_overlap", false),
@@ -64,7 +63,8 @@ LLMultiSlider::Params::Params()
triangle_color("triangle_color"),
mouse_down_callback("mouse_down_callback"),
mouse_up_callback("mouse_up_callback"),
- thumb_width("thumb_width")
+ thumb_width("thumb_width"),
+ sliders("slider")
{
name = "multi_slider_bar";
mouse_opaque(true);
@@ -85,17 +85,44 @@ LLMultiSlider::LLMultiSlider(const LLMultiSlider::Params& p)
mThumbCenterSelectedColor(p.thumb_center_selected_color()),
mDisabledThumbColor(p.thumb_disabled_color()),
mTriangleColor(p.triangle_color()),
- mThumbWidth(p.thumb_width)
+ mThumbWidth(p.thumb_width),
+ mMouseDownSignal(NULL),
+ mMouseUpSignal(NULL)
{
mValue.emptyMap();
mCurSlider = LLStringUtil::null;
if (p.mouse_down_callback.isProvided())
- initCommitCallback(p.mouse_down_callback, mMouseDownSignal);
+ {
+ setMouseDownCallback(initCommitCallback(p.mouse_down_callback));
+ }
if (p.mouse_up_callback.isProvided())
- initCommitCallback(p.mouse_up_callback, mMouseUpSignal);
+ {
+ setMouseUpCallback(initCommitCallback(p.mouse_up_callback));
+ }
+
+ for (LLInitParam::ParamIterator<SliderParams>::const_iterator it = p.sliders().begin();
+ it != p.sliders().end();
+ ++it)
+ {
+ if (it->name.isProvided())
+ {
+ addSlider(it->value, it->name);
+ }
+ else
+ {
+ addSlider(it->value);
+ }
+ }
}
+LLMultiSlider::~LLMultiSlider()
+{
+ delete mMouseDownSignal;
+ delete mMouseUpSignal;
+}
+
+
void LLMultiSlider::setSliderValue(const std::string& name, F32 value, BOOL from_event)
{
// exit if not there
@@ -218,6 +245,30 @@ const std::string& LLMultiSlider::addSlider(F32 val)
return mCurSlider;
}
+void LLMultiSlider::addSlider(F32 val, const std::string& name)
+{
+ F32 initVal = val;
+
+ if(mValue.size() >= mMaxNumSliders) {
+ return;
+ }
+
+ bool foundOne = findUnusedValue(initVal);
+ if(!foundOne) {
+ return;
+ }
+
+ // add a new thumb rect
+ mThumbRects[name] = LLRect( 0, getRect().getHeight(), mThumbWidth, 0 );
+
+ // add the value and set the current slider to this one
+ mValue.insert(name, initVal);
+ mCurSlider = name;
+
+ // move the slider
+ setSliderValue(mCurSlider, initVal, TRUE);
+}
+
bool LLMultiSlider::findUnusedValue(F32& initVal)
{
bool firstTry = true;
@@ -326,7 +377,8 @@ BOOL LLMultiSlider::handleMouseUp(S32 x, S32 y, MASK mask)
{
gFocusMgr.setMouseCapture( NULL );
- mMouseUpSignal( this, LLSD() );
+ if (mMouseUpSignal)
+ (*mMouseUpSignal)( this, LLSD() );
handled = TRUE;
make_ui_sound("UISndClickRelease");
@@ -346,7 +398,8 @@ BOOL LLMultiSlider::handleMouseDown(S32 x, S32 y, MASK mask)
{
setFocus(TRUE);
}
- mMouseDownSignal( this, LLSD() );
+ if (mMouseDownSignal)
+ (*mMouseDownSignal)( this, LLSD() );
if (MASK_CONTROL & mask) // if CTRL is modifying
{
@@ -431,7 +484,7 @@ void LLMultiSlider::draw()
F32 opacity = getEnabled() ? 1.f : 0.3f;
// Track
- LLUIImagePtr thumb_imagep = LLUI::getUIImage("rounded_square.tga");
+ LLUIImagePtr thumb_imagep = LLUI::getUIImage("Rounded_Square");
static LLUICachedControl<S32> multi_track_height ("UIMultiTrackHeight", 0);
S32 height_offset = (getRect().getHeight() - multi_track_height) / 2;
@@ -558,3 +611,14 @@ void LLMultiSlider::draw()
LLF32UICtrl::draw();
}
+boost::signals2::connection LLMultiSlider::setMouseDownCallback( const commit_signal_t::slot_type& cb )
+{
+ if (!mMouseDownSignal) mMouseDownSignal = new commit_signal_t();
+ return mMouseDownSignal->connect(cb);
+}
+
+boost::signals2::connection LLMultiSlider::setMouseUpCallback( const commit_signal_t::slot_type& cb )
+{
+ if (!mMouseUpSignal) mMouseUpSignal = new commit_signal_t();
+ return mMouseUpSignal->connect(cb);
+}
diff --git a/indra/llui/llmultislider.h b/indra/llui/llmultislider.h
index 9c01b528a7..2b422e89c9 100644
--- a/indra/llui/llmultislider.h
+++ b/indra/llui/llmultislider.h
@@ -2,31 +2,25 @@
* @file llmultislider.h
* @brief A simple multislider
*
- * $LicenseInfo:firstyear=2007&license=viewergpl$
- *
- * Copyright (c) 2007-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -41,6 +35,13 @@ class LLUICtrlFactory;
class LLMultiSlider : public LLF32UICtrl
{
public:
+ struct SliderParams : public LLInitParam::Block<SliderParams>
+ {
+ Optional<std::string> name;
+ Mandatory<F32> value;
+ SliderParams();
+ };
+
struct Params : public LLInitParam::Block<Params, LLF32UICtrl::Params>
{
Optional<S32> max_sliders;
@@ -60,6 +61,7 @@ public:
mouse_up_callback;
Optional<S32> thumb_width;
+ Multiple<SliderParams> sliders;
Params();
};
@@ -67,26 +69,28 @@ protected:
LLMultiSlider(const Params&);
friend class LLUICtrlFactory;
public:
- void setSliderValue(const std::string& name, F32 value, BOOL from_event = FALSE);
- F32 getSliderValue(const std::string& name) const;
+ virtual ~LLMultiSlider();
+ void setSliderValue(const std::string& name, F32 value, BOOL from_event = FALSE);
+ F32 getSliderValue(const std::string& name) const;
- const std::string& getCurSlider() const { return mCurSlider; }
- F32 getCurSliderValue() const { return getSliderValue(mCurSlider); }
- void setCurSlider(const std::string& name);
- void setCurSliderValue(F32 val, BOOL from_event = false) { setSliderValue(mCurSlider, val, from_event); }
+ const std::string& getCurSlider() const { return mCurSlider; }
+ F32 getCurSliderValue() const { return getSliderValue(mCurSlider); }
+ void setCurSlider(const std::string& name);
+ void setCurSliderValue(F32 val, BOOL from_event = false) { setSliderValue(mCurSlider, val, from_event); }
/*virtual*/ void setValue(const LLSD& value);
/*virtual*/ LLSD getValue() const { return mValue; }
- boost::signals::connection setMouseDownCallback( const commit_signal_t::slot_type& cb ) { return mMouseDownSignal.connect(cb); }
- boost::signals::connection setMouseUpCallback( const commit_signal_t::slot_type& cb ) { return mMouseUpSignal.connect(cb); }
+ boost::signals2::connection setMouseDownCallback( const commit_signal_t::slot_type& cb );
+ boost::signals2::connection setMouseUpCallback( const commit_signal_t::slot_type& cb );
- bool findUnusedValue(F32& initVal);
+ bool findUnusedValue(F32& initVal);
const std::string& addSlider();
const std::string& addSlider(F32 val);
- void deleteSlider(const std::string& name);
- void deleteCurSlider() { deleteSlider(mCurSlider); }
- void clear();
+ void addSlider(F32 val, const std::string& name);
+ void deleteSlider(const std::string& name);
+ void deleteCurSlider() { deleteSlider(mCurSlider); }
+ void clear();
/*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask);
/*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask);
@@ -108,7 +112,8 @@ protected:
LLRect mDragStartThumbRect;
S32 mThumbWidth;
- std::map<std::string, LLRect> mThumbRects;
+ std::map<std::string, LLRect>
+ mThumbRects;
LLUIColor mTrackColor;
LLUIColor mThumbOutlineColor;
LLUIColor mThumbCenterColor;
@@ -116,8 +121,8 @@ protected:
LLUIColor mDisabledThumbColor;
LLUIColor mTriangleColor;
- commit_signal_t mMouseDownSignal;
- commit_signal_t mMouseUpSignal;
+ commit_signal_t* mMouseDownSignal;
+ commit_signal_t* mMouseUpSignal;
};
-#endif // LL_LLSLIDER_H
+#endif // LL_MULTI_SLIDER_H
diff --git a/indra/llui/llmultisliderctrl.cpp b/indra/llui/llmultisliderctrl.cpp
index 4bbfc63976..bd65625f53 100644
--- a/indra/llui/llmultisliderctrl.cpp
+++ b/indra/llui/llmultisliderctrl.cpp
@@ -2,31 +2,25 @@
* @file llmultisliderctrl.cpp
* @brief LLMultiSliderCtrl base class
*
- * $LicenseInfo:firstyear=2007&license=viewergpl$
- *
- * Copyright (c) 2007-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -34,9 +28,6 @@
#include "llmultisliderctrl.h"
-#include "audioengine.h"
-#include "sound_ids.h"
-
#include "llmath.h"
#include "llfontgl.h"
#include "llgl.h"
@@ -52,7 +43,7 @@
#include "llresmgr.h"
#include "lluictrlfactory.h"
-static LLDefaultWidgetRegistry::Register<LLMultiSliderCtrl> r("multi_slider");
+static LLDefaultChildRegistry::Register<LLMultiSliderCtrl> r("multi_slider");
const U32 MAX_STRING_LENGTH = 10;
LLMultiSliderCtrl::Params::Params()
@@ -68,7 +59,8 @@ LLMultiSliderCtrl::Params::Params()
text_color("text_color"),
text_disabled_color("text_disabled_color"),
mouse_down_callback("mouse_down_callback"),
- mouse_up_callback("mouse_up_callback")
+ mouse_up_callback("mouse_up_callback"),
+ sliders("slider")
{
mouse_opaque = true;
}
@@ -101,7 +93,7 @@ LLMultiSliderCtrl::LLMultiSliderCtrl(const LLMultiSliderCtrl::Params& p)
LLTextBox::Params params;
params.name("MultiSliderCtrl Label");
params.rect(label_rect);
- params.text(p.label);
+ params.initial_value(p.label());
params.font(p.font);
mLabelBox = LLUICtrlFactory::create<LLTextBox> (params);
addChild(mLabelBox);
@@ -140,10 +132,10 @@ 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( &LLMultiSliderCtrl::onEditorGainFocus );
+ mEditor->setFocusReceivedCallback( boost::bind(LLMultiSliderCtrl::onEditorGainFocus, _1, this) );
// don't do this, as selecting the entire text is single clicking in some cases
// and double clicking in others
//mEditor->setSelectAllonFocusReceived(TRUE);
@@ -164,6 +156,7 @@ LLMultiSliderCtrl::LLMultiSliderCtrl(const LLMultiSliderCtrl::Params& p)
S32 slider_left = label_width ? label_width + multi_sliderctrl_spacing : 0;
LLRect slider_rect( slider_left, top, slider_right, bottom );
LLMultiSlider::Params params;
+ params.sliders = p.sliders;
params.rect(slider_rect);
params.commit_callback.function( LLMultiSliderCtrl::onSliderCommit );
params.mouse_down_callback( p.mouse_down_callback );
@@ -331,9 +324,14 @@ void LLMultiSliderCtrl::updateText()
// static
void LLMultiSliderCtrl::onEditorCommit( LLUICtrl* ctrl, const LLSD& userdata)
{
- LLMultiSliderCtrl* self = dynamic_cast<LLMultiSliderCtrl*>(ctrl);
+ 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;
@@ -347,7 +345,7 @@ void LLMultiSliderCtrl::onEditorCommit( LLUICtrl* ctrl, const LLSD& userdata)
if( self->mMultiSlider->getMinValue() <= val && val <= self->mMultiSlider->getMaxValue() )
{
self->setCurSliderValue( val ); // set the value temporarily so that the callback can retrieve it.
- if( self->mValidateSignal( self, val ) )
+ if( !self->mValidateSignal || (*(self->mValidateSignal))( self, val ) )
{
success = TRUE;
}
@@ -372,7 +370,7 @@ void LLMultiSliderCtrl::onEditorCommit( LLUICtrl* ctrl, const LLSD& userdata)
// static
void LLMultiSliderCtrl::onSliderCommit(LLUICtrl* ctrl, const LLSD& userdata)
{
- LLMultiSliderCtrl* self = dynamic_cast<LLMultiSliderCtrl*>(ctrl);
+ LLMultiSliderCtrl* self = dynamic_cast<LLMultiSliderCtrl*>(ctrl->getParent());
if (!self)
return;
@@ -381,7 +379,7 @@ void LLMultiSliderCtrl::onSliderCommit(LLUICtrl* ctrl, const LLSD& userdata)
F32 new_val = self->mMultiSlider->getCurSliderValue();
self->mCurValue = new_val; // set the value temporarily so that the callback can retrieve it.
- if( self->mValidateSignal( self, new_val ) )
+ if( !self->mValidateSignal || (*(self->mValidateSignal))( self, new_val ) )
{
success = TRUE;
}
@@ -460,12 +458,12 @@ void LLMultiSliderCtrl::setPrecision(S32 precision)
updateText();
}
-boost::signals::connection LLMultiSliderCtrl::setSliderMouseDownCallback( const commit_signal_t::slot_type& cb )
+boost::signals2::connection LLMultiSliderCtrl::setSliderMouseDownCallback( const commit_signal_t::slot_type& cb )
{
return mMultiSlider->setMouseDownCallback( cb );
}
-boost::signals::connection LLMultiSliderCtrl::setSliderMouseUpCallback( const commit_signal_t::slot_type& cb )
+boost::signals2::connection LLMultiSliderCtrl::setSliderMouseUpCallback( const commit_signal_t::slot_type& cb )
{
return mMultiSlider->setMouseUpCallback( cb );
}
diff --git a/indra/llui/llmultisliderctrl.h b/indra/llui/llmultisliderctrl.h
index 85ba77b7df..b6a3542376 100644
--- a/indra/llui/llmultisliderctrl.h
+++ b/indra/llui/llmultisliderctrl.h
@@ -2,31 +2,25 @@
* @file llmultisliderctrl.h
* @brief LLMultiSliderCtrl base class
*
- * $LicenseInfo:firstyear=2007&license=viewergpl$
- *
- * Copyright (c) 2007-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -69,6 +63,8 @@ public:
Optional<CommitCallbackParam> mouse_down_callback,
mouse_up_callback;
+ Multiple<LLMultiSlider::SliderParams> sliders;
+
Params();
};
@@ -90,8 +86,8 @@ public:
void setCurSlider(const std::string& name);
void setCurSliderValue(F32 val, BOOL from_event = false) { setSliderValue(mMultiSlider->getCurSlider(), val, from_event); }
- virtual void setMinValue(LLSD min_value) { setMinValue((F32)min_value.asReal()); }
- virtual void setMaxValue(LLSD max_value) { setMaxValue((F32)max_value.asReal()); }
+ virtual void setMinValue(const LLSD& min_value) { setMinValue((F32)min_value.asReal()); }
+ virtual void setMaxValue(const LLSD& max_value) { setMaxValue((F32)max_value.asReal()); }
BOOL isMouseHeldDown();
@@ -108,15 +104,15 @@ public:
void deleteSlider(const std::string& name);
void deleteCurSlider() { deleteSlider(mMultiSlider->getCurSlider()); }
- F32 getMinValue() { return mMultiSlider->getMinValue(); }
- F32 getMaxValue() { return mMultiSlider->getMaxValue(); }
+ F32 getMinValue() const { return mMultiSlider->getMinValue(); }
+ F32 getMaxValue() const { return mMultiSlider->getMaxValue(); }
void setLabel(const std::string& label) { if (mLabelBox) mLabelBox->setText(label); }
void setLabelColor(const LLColor4& c) { mTextEnabledColor = c; }
void setDisabledLabelColor(const LLColor4& c) { mTextDisabledColor = c; }
- boost::signals::connection setSliderMouseDownCallback( const commit_signal_t::slot_type& cb );
- boost::signals::connection setSliderMouseUpCallback( const commit_signal_t::slot_type& cb );
+ boost::signals2::connection setSliderMouseDownCallback( const commit_signal_t::slot_type& cb );
+ boost::signals2::connection setSliderMouseUpCallback( const commit_signal_t::slot_type& cb );
virtual void onTabInto();
diff --git a/indra/llui/llnotificationptr.h b/indra/llui/llnotificationptr.h
new file mode 100644
index 0000000000..acc047527f
--- /dev/null
+++ b/indra/llui/llnotificationptr.h
@@ -0,0 +1,35 @@
+/**
+ * @file llnotificationptr.h
+ *
+ * $LicenseInfo:firstyear=2008&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+#ifndef LLNOTIFICATIONPTR_H
+#define LLNOTIFICATIONPTR_H
+
+// Many classes just store a single LLNotificationPtr
+// and llnotifications.h is very large, so define this ligher header.
+#include <boost/shared_ptr.hpp>
+
+class LLNotification;
+typedef boost::shared_ptr<LLNotification> LLNotificationPtr;
+
+#endif
diff --git a/indra/llui/llnotifications.cpp b/indra/llui/llnotifications.cpp
index 452f18b40b..99d540a9de 100644
--- a/indra/llui/llnotifications.cpp
+++ b/indra/llui/llnotifications.cpp
@@ -2,31 +2,25 @@
* @file llnotifications.cpp
* @brief Non-UI queue manager for keeping a prioritized list of notifications
*
-* $LicenseInfo:firstyear=2008&license=viewergpl$
-*
-* Copyright (c) 2008-2009, Linden Research, Inc.
-*
+* $LicenseInfo:firstyear=2008&license=viewerlgpl$
* Second Life Viewer Source Code
-* The source code in this file ("Source Code") is provided by Linden Lab
-* to you under the terms of the GNU General Public License, version 2.0
-* ("GPL"), unless you have obtained a separate licensing agreement
-* ("Other License"), formally executed by you and Linden Lab. Terms of
-* the GPL can be found in doc/GPL-license.txt in this distribution, or
-* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+* Copyright (C) 2010, Linden Research, Inc.
*
-* There are special exceptions to the terms and conditions of the GPL as
-* it is applied to this Source Code. View the full text of the exception
-* in the file doc/FLOSS-exception.txt in this software distribution, or
-* online at
-* http://secondlifegrid.net/programs/open_source/licensing/flossexception
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation;
+* version 2.1 of the License only.
*
-* By copying, modifying or distributing this software, you acknowledge
-* that you have read and understood your obligations described above,
-* and agree to abide by those obligations.
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
*
-* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
-* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
-* COMPLETENESS OR PERFORMANCE.
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -34,11 +28,15 @@
#include "llnotifications.h"
+#include "llinstantmessage.h"
+#include "llxmlnode.h"
#include "lluictrl.h"
#include "lluictrlfactory.h"
#include "lldir.h"
#include "llsdserialize.h"
#include "lltrans.h"
+#include "llnotificationslistener.h"
+#include "llstring.h"
#include <algorithm>
#include <boost/regex.hpp>
@@ -46,120 +44,48 @@
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)
- {
- return !pNotification->isCancelled();
- }
- void savePersistentNotifications()
+ // The channel gets all persistent notifications except those that have been canceled
+ static bool notificationFilter(LLNotificationPtr pNotification)
{
- llinfos << "Saving open notifications to " << mFileName << llendl;
-
- 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;
-
- // 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);
- }
-
- 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;
- }
+ bool handle_notification = false;
- 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;
+ handle_notification = pNotification->isPersistent()
+ && !pNotification->isCancelled();
- 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)));
- }
+ return handle_notification;
}
- //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)
{
+ // filter everything if we are to ignore ALL
+ if(LLNotifications::instance().getIgnoreAllNotifications())
+ {
+ return false;
+ }
+
LLNotificationFormPtr form = notification->getForm();
// Check to see if the user wants to ignore this alert
if (form->getIgnoreType() != LLNotificationForm::IGNORE_NO)
@@ -231,7 +157,7 @@ LLNotificationForm::LLNotificationForm(const std::string& name, const LLXMLNodeP
LLSD item_entry;
std::string element_name = child->getName()->mString;
- if (element_name == "ignore")
+ if (element_name == "ignore" )
{
bool save_option = false;
child->getAttribute_bool("save_option", save_option);
@@ -269,6 +195,7 @@ LLNotificationForm::LLNotificationForm(const std::string& name, const LLXMLNodeP
}
LLNotificationForm::LLNotificationForm(const LLSD& sd)
+ : mIgnore(IGNORE_NO)
{
if (sd.isArray())
{
@@ -370,7 +297,8 @@ LLNotificationTemplate::LLNotificationTemplate() :
mExpireSeconds(0),
mExpireOption(-1),
mURLOption(-1),
- mURLOpenExternally(-1),
+ mURLOpenExternally(-1),
+ mPersist(false),
mUnique(false),
mPriority(NOTIFICATION_PRIORITY_NORMAL)
{
@@ -378,7 +306,7 @@ LLNotificationTemplate::LLNotificationTemplate() :
}
LLNotification::LLNotification(const LLNotification::Params& p) :
- mTimestamp(p.timestamp),
+ mTimestamp(p.time_stamp),
mSubstitutions(p.substitutions),
mPayload(p.payload),
mExpiresAt(0),
@@ -386,7 +314,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())
{
@@ -399,6 +329,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);
@@ -409,7 +348,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"];
@@ -428,6 +369,7 @@ LLNotification::LLNotification(const LLSD& sd) :
LLSD LLNotification::asLLSD()
{
LLSD output;
+ output["id"] = mId;
output["name"] = mTemplatep->mName;
output["form"] = getForm()->asLLSD();
output["substitutions"] = mSubstitutions;
@@ -436,6 +378,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;
}
@@ -463,7 +412,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();
}
@@ -540,14 +491,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 = "";
@@ -581,19 +542,19 @@ void LLNotification::setResponseFunctor(std::string const &responseFunctorName)
mTemporaryResponder = false;
}
-bool LLNotification::payloadContainsAll(const std::vector<std::string>& required_fields) const
+void LLNotification::setResponseFunctor(const LLNotificationFunctorRegistry::ResponseFunctor& cb)
{
- for(std::vector<std::string>::const_iterator required_fields_it = required_fields.begin();
- required_fields_it != required_fields.end();
- required_fields_it++)
+ if(mTemporaryResponder)
{
- std::string required_field_name = *required_fields_it;
- if( ! getPayload().has(required_field_name))
- {
- return false; // a required field was not found
- }
+ LLNotificationFunctorRegistry::instance().unregisterFunctor(mResponseFunctorName);
}
- return true; // all required fields were found
+
+ LLNotificationFunctorRegistry::instance().registerFunctor(mResponseFunctorName, cb);
+}
+
+void LLNotification::setResponseFunctor(const LLNotificationResponderPtr& responder)
+{
+ mResponder = responder;
}
bool LLNotification::isEquivalentTo(LLNotificationPtr that) const
@@ -604,11 +565,22 @@ bool LLNotification::isEquivalentTo(LLNotificationPtr that) const
}
if (this->mTemplatep->mUnique)
{
+ const LLSD& these_substitutions = this->getSubstitutions();
+ const LLSD& those_substitutions = that->getSubstitutions();
+
// highlander bit sez there can only be one of these
- return
- this->payloadContainsAll(that->mTemplatep->mUniqueContext) &&
- that->payloadContainsAll(this->mTemplatep->mUniqueContext);
+ for (std::vector<std::string>::const_iterator it = mTemplatep->mUniqueContext.begin(), end_it = mTemplatep->mUniqueContext.end();
+ it != end_it;
+ ++it)
+ {
+ if (these_substitutions.get(*it).asString() != those_substitutions.get(*it).asString())
+ {
+ return false;
+ }
+ }
+ return true;
}
+
return false;
}
@@ -697,13 +669,22 @@ LLBoundListener LLNotificationChannelBase::connectChangedImpl(const LLEventListe
// only about new notifications
for (LLNotificationSet::iterator it = mItems.begin(); it != mItems.end(); ++it)
{
- slot(LLSD().insert("sigtype", "load").insert("id", (*it)->id()));
+ slot(LLSD().with("sigtype", "load").with("id", (*it)->id()));
}
// and then connect the signal so that all future notifications will also be
// forwarded.
return mChanged.connect(slot);
}
+LLBoundListener LLNotificationChannelBase::connectAtFrontChangedImpl(const LLEventListener& slot)
+{
+ for (LLNotificationSet::iterator it = mItems.begin(); it != mItems.end(); ++it)
+ {
+ slot(LLSD().with("sigtype", "load").with("id", (*it)->id()));
+ }
+ return mChanged.connect(slot, boost::signals2::at_front);
+}
+
LLBoundListener LLNotificationChannelBase::connectPassedFilterImpl(const LLEventListener& slot)
{
// these two filters only fire for notifications added after the current one, because
@@ -831,8 +812,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;
@@ -884,7 +869,7 @@ void LLNotificationChannel::setComparator(LLNotificationComparator comparator)
mItems.swap(s2);
// notify clients that we've been resorted
- mChanged(LLSD().insert("sigtype", "sort"));
+ mChanged(LLSD().with("sigtype", "sort"));
}
bool LLNotificationChannel::isEmpty() const
@@ -925,9 +910,12 @@ std::string LLNotificationChannel::summarize()
// LLNotifications implementation
// ---
LLNotifications::LLNotifications() : LLNotificationChannelBase(LLNotificationFilters::includeEverything,
- LLNotificationComparators::orderByUUID())
+ LLNotificationComparators::orderByUUID()),
+ mIgnoreAllNotifications(false)
{
LLUICtrl::CommitCallbackRegistry::currentRegistrar().add("Notification.Show", boost::bind(&LLNotifications::addFromCallback, this, _2));
+
+ mListener.reset(new LLNotificationsListener(*this));
}
@@ -1032,6 +1020,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;
}
@@ -1059,20 +1048,20 @@ 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")->
connectChanged(boost::bind(&LLNotifications::expirationHandler, this, _1));
+ // uniqueHandler slot should be added as first slot of the signal due to
+ // usage LLStopWhenHandled combiner in LLStandardSignal
LLNotifications::instance().getChannel("Unique")->
- connectChanged(boost::bind(&LLNotifications::uniqueHandler, this, _1));
- LLNotifications::instance().getChannel("Unique")->
- connectFailedFilter(boost::bind(&LLNotifications::failedUniquenessTest, this, _1));
+ connectAtFrontChanged(boost::bind(&LLNotifications::uniqueHandler, this, _1));
+// failedUniquenessTest slot isn't necessary
+// LLNotifications::instance().getChannel("Unique")->
+// connectFailedFilter(boost::bind(&LLNotifications::failedUniquenessTest, this, _1));
LLNotifications::instance().getChannel("Ignore")->
connectFailedFilter(&handleIgnoredNotification);
}
@@ -1366,10 +1355,9 @@ void LLNotifications::addFromCallback(const LLSD& name)
add(LLNotification::Params().name(name.asString()));
}
-// we provide a couple of simple add notification functions so that it's reasonable to create notifications in one line
LLNotificationPtr LLNotifications::add(const std::string& name,
- const LLSD& substitutions,
- const LLSD& payload)
+ const LLSD& substitutions,
+ const LLSD& payload)
{
LLNotification::Params::Functor functor_p;
functor_p.name = name;
@@ -1377,15 +1365,16 @@ LLNotificationPtr LLNotifications::add(const std::string& name,
}
LLNotificationPtr LLNotifications::add(const std::string& name,
- const LLSD& substitutions,
- const LLSD& payload,
- const std::string& functor_name)
+ const LLSD& substitutions,
+ const LLSD& payload,
+ const std::string& functor_name)
{
LLNotification::Params::Functor functor_p;
functor_p.name = functor_name;
return add(LLNotification::Params().name(name).substitutions(substitutions).payload(payload).functor(functor_p));
}
-
+
+//virtual
LLNotificationPtr LLNotifications::add(const std::string& name,
const LLSD& substitutions,
const LLSD& payload,
@@ -1414,7 +1403,7 @@ void LLNotifications::add(const LLNotificationPtr pNotif)
llerrs << "Notification added a second time to the master notification channel." << llendl;
}
- updateItem(LLSD().insert("sigtype", "add").insert("id", pNotif->id()), pNotif);
+ updateItem(LLSD().with("sigtype", "add").with("id", pNotif->id()), pNotif);
}
void LLNotifications::cancel(LLNotificationPtr pNotif)
@@ -1424,8 +1413,8 @@ void LLNotifications::cancel(LLNotificationPtr pNotif)
{
llerrs << "Attempted to delete nonexistent notification " << pNotif->getName() << llendl;
}
- updateItem(LLSD().insert("sigtype", "delete").insert("id", pNotif->id()), pNotif);
pNotif->cancel();
+ updateItem(LLSD().with("sigtype", "delete").with("id", pNotif->id()), pNotif);
}
void LLNotifications::update(const LLNotificationPtr pNotif)
@@ -1433,7 +1422,7 @@ void LLNotifications::update(const LLNotificationPtr pNotif)
LLNotificationSet::iterator it=mItems.find(pNotif);
if (it != mItems.end())
{
- updateItem(LLSD().insert("sigtype", "change").insert("id", pNotif->id()), pNotif);
+ updateItem(LLSD().with("sigtype", "change").with("id", pNotif->id()), pNotif);
}
}
@@ -1473,6 +1462,14 @@ std::string LLNotifications::getGlobalString(const std::string& key) const
}
}
+void LLNotifications::setIgnoreAllNotifications(bool setting)
+{
+ mIgnoreAllNotifications = setting;
+}
+bool LLNotifications::getIgnoreAllNotifications()
+{
+ return mIgnoreAllNotifications;
+}
// ---
// END OF LLNotifications implementation
@@ -1484,3 +1481,18 @@ 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)
+{
+ mName = first + " " + last;
+
+ LLStringUtil::trim(mName);
+ if (mName.empty())
+ {
+ llwarns << "Empty name received for Id: " << id << llendl;
+ mName = SYSTEM_FROM;
+ }
+ modifyNotificationParams();
+ LLNotifications::instance().add(mParams);
+ cleanup();
+}
diff --git a/indra/llui/llnotifications.h b/indra/llui/llnotifications.h
index 5c8d146e0c..2cc8803f10 100644
--- a/indra/llui/llnotifications.h
+++ b/indra/llui/llnotifications.h
@@ -3,31 +3,25 @@
* @brief Non-UI manager and support for keeping a prioritized list of notifications
* @author Q (with assistance from Richard and Coco)
*
-* $LicenseInfo:firstyear=2008&license=viewergpl$
-*
-* Copyright (c) 2008-2009, Linden Research, Inc.
-*
+* $LicenseInfo:firstyear=2008&license=viewerlgpl$
* Second Life Viewer Source Code
-* The source code in this file ("Source Code") is provided by Linden Lab
-* to you under the terms of the GNU General Public License, version 2.0
-* ("GPL"), unless you have obtained a separate licensing agreement
-* ("Other License"), formally executed by you and Linden Lab. Terms of
-* the GPL can be found in doc/GPL-license.txt in this distribution, or
-* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+* Copyright (C) 2010, Linden Research, Inc.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation;
+* version 2.1 of the License only.
*
-* There are special exceptions to the terms and conditions of the GPL as
-* it is applied to this Source Code. View the full text of the exception
-* in the file doc/FLOSS-exception.txt in this software distribution, or
-* online at
-* http://secondlifegrid.net/programs/open_source/licensing/flossexception
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
*
-* 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.
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
-* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
-* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
-* COMPLETENESS OR PERFORMANCE.
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -100,11 +94,11 @@
// and we need this to manage the notification callbacks
#include "llevents.h"
#include "llfunctorregistry.h"
-#include "llui.h"
-#include "llmemory.h"
-
-class LLNotification;
-typedef boost::shared_ptr<LLNotification> LLNotificationPtr;
+#include "llpointer.h"
+#include "llinitparam.h"
+#include "llnotificationslistener.h"
+#include "llnotificationptr.h"
+#include "llcachename.h"
typedef enum e_notification_priority
@@ -116,8 +110,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;
@@ -158,7 +167,8 @@ public:
LLNotificationForm();
LLNotificationForm(const LLSD& sd);
- LLNotificationForm(const std::string& name, const LLXMLNodePtr xml_node);
+ LLNotificationForm(const std::string& name,
+ const LLPointer<class LLXMLNode> xml_node);
LLSD asLLSD() const;
@@ -294,17 +304,20 @@ public:
Optional<LLSD> payload;
Optional<ENotificationPriority> priority;
Optional<LLSD> form_elements;
- Optional<LLDate> timestamp;
+ Optional<LLDate> time_stamp;
Optional<LLNotificationContext*> context;
+ Optional<void*> responder;
struct Functor : public LLInitParam::Choice<Functor>
{
- Option<std::string> name;
- Option<LLNotificationFunctorRegistry::ResponseFunctor> function;
+ Alternative<std::string> name;
+ Alternative<LLNotificationFunctorRegistry::ResponseFunctor> function;
+ Alternative<LLNotificationResponderPtr> responder;
Functor()
: name("functor_name"),
- function("functor")
+ function("functor"),
+ responder("responder")
{}
};
Optional<Functor> functor;
@@ -312,22 +325,30 @@ public:
Params()
: name("name"),
priority("priority", NOTIFICATION_PRIORITY_UNSPECIFIED),
- timestamp("time_stamp")
+ time_stamp("time_stamp"),
+ payload("payload"),
+ form_elements("form_elements")
{
- timestamp = LLDate::now();
+ time_stamp = LLDate::now();
+ responder = NULL;
}
Params(const std::string& _name)
- : name("name"),
- priority("priority", NOTIFICATION_PRIORITY_UNSPECIFIED),
- timestamp("time_stamp")
+ : name("name"),
+ priority("priority", NOTIFICATION_PRIORITY_UNSPECIFIED),
+ time_stamp("time_stamp"),
+ payload("payload"),
+ form_elements("form_elements")
{
functor.name = _name;
name = _name;
- timestamp = LLDate::now();
+ time_stamp = LLDate::now();
+ responder = NULL;
}
};
+ LLNotificationResponderPtr getResponderPtr() { return mResponder; }
+
private:
LLUUID mId;
@@ -337,13 +358,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;
@@ -367,12 +392,10 @@ 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();
- bool payloadContainsAll(const std::vector<std::string>& required_fields) const;
-
public:
// constructor from a saved notification
@@ -380,6 +403,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,
@@ -419,6 +446,10 @@ public:
void respond(const LLSD& sd);
+ void* getResponder() { return mResponderObj; }
+
+ void setResponder(void* responder) { mResponderObj = responder; }
+
void setIgnored(bool ignore);
bool isCancelled() const
@@ -431,6 +462,8 @@ public:
return mRespondedTo;
}
+ const LLSD& getResponse() { return mResponse; }
+
bool isIgnored() const
{
return mIgnored;
@@ -440,7 +473,12 @@ public:
{
return mTemplatep->mName;
}
-
+
+ bool isPersistent() const
+ {
+ return mTemplatep->mPersist;
+ }
+
const LLUUID& id() const
{
return mId;
@@ -500,6 +538,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)
@@ -617,7 +659,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)
@@ -689,6 +731,14 @@ public:
this,
_1));
}
+ template <typename LISTENER>
+ LLBoundListener connectAtFrontChanged(const LISTENER& slot)
+ {
+ return LLEventDetail::visit_and_connect(slot,
+ boost::bind(&LLNotificationChannelBase::connectAtFrontChangedImpl,
+ this,
+ _1));
+ }
template <typename LISTENER>
LLBoundListener connectPassedFilter(const LISTENER& slot)
{
@@ -714,6 +764,7 @@ public:
protected:
LLBoundListener connectChangedImpl(const LLEventListener& slot);
+ LLBoundListener connectAtFrontChangedImpl(const LLEventListener& slot);
LLBoundListener connectPassedFilterImpl(const LLEventListener& slot);
LLBoundListener connectFailedFilterImpl(const LLEventListener& slot);
@@ -798,9 +849,19 @@ private:
LLNotificationComparator mComparator;
};
-
+// An interface class to provide a clean linker seam to the LLNotifications class.
+// Extend this interface as needed for your use of LLNotifications.
+class LLNotificationsInterface
+{
+public:
+ virtual LLNotificationPtr add(const std::string& name,
+ const LLSD& substitutions,
+ const LLSD& payload,
+ LLNotificationFunctorRegistry::ResponseFunctor functor) = 0;
+};
class LLNotifications :
+ public LLNotificationsInterface,
public LLSingleton<LLNotifications>,
public LLNotificationChannelBase
{
@@ -811,20 +872,21 @@ public:
// load notification descriptions from file;
// OK to call more than once because it will reload
bool loadTemplates();
- LLXMLNodePtr checkForXMLTemplate(LLXMLNodePtr item);
+ LLPointer<class LLXMLNode> checkForXMLTemplate(LLPointer<class LLXMLNode> item);
// Add a simple notification (from XUI)
void addFromCallback(const LLSD& name);
- // we provide a collection of simple add notification functions so that it's reasonable to create notifications in one line
+ // *NOTE: To add simple notifications, #include "llnotificationsutil.h"
+ // and use LLNotificationsUtil::add("MyNote") or add("MyNote", args)
LLNotificationPtr add(const std::string& name,
- const LLSD& substitutions = LLSD(),
- const LLSD& payload = LLSD());
+ const LLSD& substitutions,
+ const LLSD& payload);
LLNotificationPtr add(const std::string& name,
const LLSD& substitutions,
const LLSD& payload,
const std::string& functor_name);
- LLNotificationPtr add(const std::string& name,
+ /* virtual */ LLNotificationPtr add(const std::string& name,
const LLSD& substitutions,
const LLSD& payload,
LLNotificationFunctorRegistry::ResponseFunctor functor);
@@ -870,6 +932,9 @@ public:
std::string getGlobalString(const std::string& key) const;
+ void setIgnoreAllNotifications(bool ignore);
+ bool getIgnoreAllNotifications();
+
private:
// we're a singleton, so we don't have a public constructor
LLNotifications();
@@ -891,15 +956,75 @@ private:
std::string mFileName;
- typedef std::map<std::string, LLXMLNodePtr> XMLTemplateMap;
+ typedef std::map<std::string, LLPointer<class LLXMLNode> > XMLTemplateMap;
XMLTemplateMap mXmlTemplates;
LLNotificationMap mUniqueNotifications;
typedef std::map<std::string, std::string> GlobalStringMap;
GlobalStringMap mGlobalStrings;
+
+ bool mIgnoreAllNotifications;
+
+ 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/llnotificationslistener.cpp b/indra/llui/llnotificationslistener.cpp
new file mode 100644
index 0000000000..44a90398fd
--- /dev/null
+++ b/indra/llui/llnotificationslistener.cpp
@@ -0,0 +1,349 @@
+/**
+ * @file llnotificationslistener.cpp
+ * @author Brad Kittenbrink
+ * @date 2009-07-08
+ * @brief Implementation for llnotificationslistener.
+ *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+#include "llnotificationslistener.h"
+#include "llnotifications.h"
+#include "llsd.h"
+#include "llui.h"
+
+LLNotificationsListener::LLNotificationsListener(LLNotifications & notifications) :
+ LLEventAPI("LLNotifications",
+ "LLNotifications listener to (e.g.) pop up a notification"),
+ mNotifications(notifications)
+{
+ add("requestAdd",
+ "Add a notification with specified [\"name\"], [\"substitutions\"] and [\"payload\"].\n"
+ "If optional [\"reply\"] specified, arrange to send user response on that LLEventPump.",
+ &LLNotificationsListener::requestAdd);
+ add("listChannels",
+ "Post to [\"reply\"] a map of info on existing channels",
+ &LLNotificationsListener::listChannels,
+ LLSD().with("reply", LLSD()));
+ add("listChannelNotifications",
+ "Post to [\"reply\"] an array of info on notifications in channel [\"channel\"]",
+ &LLNotificationsListener::listChannelNotifications,
+ LLSD().with("reply", LLSD()).with("channel", LLSD()));
+ add("respond",
+ "Respond to notification [\"uuid\"] with data in [\"response\"]",
+ &LLNotificationsListener::respond,
+ LLSD().with("uuid", LLSD()));
+ add("cancel",
+ "Cancel notification [\"uuid\"]",
+ &LLNotificationsListener::cancel,
+ LLSD().with("uuid", LLSD()));
+ add("ignore",
+ "Ignore future notification [\"name\"]\n"
+ "(from <notification name= > in notifications.xml)\n"
+ "according to boolean [\"ignore\"].\n"
+ "If [\"name\"] is omitted or undefined, [un]ignore all future notifications.\n"
+ "Note that ignored notifications are not forwarded unless intercepted before\n"
+ "the \"Ignore\" channel.",
+ &LLNotificationsListener::ignore);
+ add("forward",
+ "Forward to [\"pump\"] future notifications on channel [\"channel\"]\n"
+ "according to boolean [\"forward\"]. When enabled, only types matching\n"
+ "[\"types\"] are forwarded, as follows:\n"
+ "omitted or undefined: forward all notifications\n"
+ "string: forward only the specific named [sig]type\n"
+ "array of string: forward any notification matching any named [sig]type.\n"
+ "When boolean [\"respond\"] is true, we auto-respond to each forwarded\n"
+ "notification.",
+ &LLNotificationsListener::forward,
+ LLSD().with("channel", LLSD()));
+}
+
+// This is here in the .cpp file so we don't need the definition of class
+// Forwarder in the header file.
+LLNotificationsListener::~LLNotificationsListener()
+{
+}
+
+void LLNotificationsListener::requestAdd(const LLSD& event_data) const
+{
+ if(event_data.has("reply"))
+ {
+ mNotifications.add(event_data["name"],
+ event_data["substitutions"],
+ event_data["payload"],
+ boost::bind(&LLNotificationsListener::NotificationResponder,
+ this,
+ event_data["reply"].asString(),
+ _1, _2
+ )
+ );
+ }
+ else
+ {
+ mNotifications.add(event_data["name"],
+ event_data["substitutions"],
+ event_data["payload"]);
+ }
+}
+
+void LLNotificationsListener::NotificationResponder(const std::string& reply_pump,
+ const LLSD& notification,
+ const LLSD& response) const
+{
+ LLSD reponse_event;
+ reponse_event["notification"] = notification;
+ reponse_event["response"] = response;
+ LLEventPumps::getInstance()->obtain(reply_pump).post(reponse_event);
+}
+
+void LLNotificationsListener::listChannels(const LLSD& params) const
+{
+ LLReqID reqID(params);
+ LLSD response(reqID.makeResponse());
+ for (LLNotifications::ChannelMap::const_iterator cmi(mNotifications.mChannels.begin()),
+ cmend(mNotifications.mChannels.end());
+ cmi != cmend; ++cmi)
+ {
+ LLSD channelInfo;
+ channelInfo["parent"] = cmi->second->getParentChannelName();
+ response[cmi->first] = channelInfo;
+ }
+ LLEventPumps::instance().obtain(params["reply"]).post(response);
+}
+
+void LLNotificationsListener::listChannelNotifications(const LLSD& params) const
+{
+ LLReqID reqID(params);
+ LLSD response(reqID.makeResponse());
+ LLNotificationChannelPtr channel(mNotifications.getChannel(params["channel"]));
+ if (channel)
+ {
+ LLSD notifications(LLSD::emptyArray());
+ for (LLNotificationChannel::Iterator ni(channel->begin()), nend(channel->end());
+ ni != nend; ++ni)
+ {
+ notifications.append(asLLSD(*ni));
+ }
+ response["notifications"] = notifications;
+ }
+ LLEventPumps::instance().obtain(params["reply"]).post(response);
+}
+
+void LLNotificationsListener::respond(const LLSD& params) const
+{
+ LLNotificationPtr notification(mNotifications.find(params["uuid"]));
+ if (notification)
+ {
+ notification->respond(params["response"]);
+ }
+}
+
+void LLNotificationsListener::cancel(const LLSD& params) const
+{
+ LLNotificationPtr notification(mNotifications.find(params["uuid"]));
+ if (notification)
+ {
+ mNotifications.cancel(notification);
+ }
+}
+
+void LLNotificationsListener::ignore(const LLSD& params) const
+{
+ // Calling a method named "ignore", but omitting its "ignore" Boolean
+ // argument, should by default cause something to be ignored. Explicitly
+ // pass ["ignore"] = false to cancel ignore.
+ bool ignore = true;
+ if (params.has("ignore"))
+ {
+ ignore = params["ignore"].asBoolean();
+ }
+ // This method can be used to affect either a single notification name or
+ // all future notifications. The two use substantially different mechanisms.
+ if (params["name"].isDefined())
+ {
+ // ["name"] was passed: ignore just that notification
+ LLUI::sSettingGroups["ignores"]->setBOOL(params["name"], ignore);
+ }
+ else
+ {
+ // no ["name"]: ignore all future notifications
+ mNotifications.setIgnoreAllNotifications(ignore);
+ }
+}
+
+class LLNotificationsListener::Forwarder: public LLEventTrackable
+{
+ LOG_CLASS(LLNotificationsListener::Forwarder);
+public:
+ Forwarder(LLNotifications& llnotifications, const std::string& channel):
+ mNotifications(llnotifications),
+ mRespond(false)
+ {
+ // Connect to the specified channel on construction. Because
+ // LLEventTrackable is a base, we should automatically disconnect when
+ // destroyed.
+ LLNotificationChannelPtr channelptr(llnotifications.getChannel(channel));
+ if (channelptr)
+ {
+ // Insert our processing as a "passed filter" listener. This way
+ // we get to run before all the "changed" listeners, and we get to
+ // swipe it (hide it from the other listeners) if desired.
+ channelptr->connectPassedFilter(boost::bind(&Forwarder::handle, this, _1));
+ }
+ }
+
+ void setPumpName(const std::string& name) { mPumpName = name; }
+ void setTypes(const LLSD& types) { mTypes = types; }
+ void setRespond(bool respond) { mRespond = respond; }
+
+private:
+ bool handle(const LLSD& notification) const;
+ bool matchType(const LLSD& filter, const std::string& type) const;
+
+ LLNotifications& mNotifications;
+ std::string mPumpName;
+ LLSD mTypes;
+ bool mRespond;
+};
+
+void LLNotificationsListener::forward(const LLSD& params)
+{
+ std::string channel(params["channel"]);
+ // First decide whether we're supposed to start forwarding or stop it.
+ // Default to true.
+ bool forward = true;
+ if (params.has("forward"))
+ {
+ forward = params["forward"].asBoolean();
+ }
+ if (! forward)
+ {
+ // This is a request to stop forwarding notifications on the specified
+ // channel. The rest of the params don't matter.
+ // Because mForwarders contains scoped_ptrs, erasing the map entry
+ // DOES delete the heap Forwarder object. Because Forwarder derives
+ // from LLEventTrackable, destroying it disconnects it from the
+ // channel.
+ mForwarders.erase(channel);
+ return;
+ }
+ // From here on, we know we're being asked to start (or modify) forwarding
+ // on the specified channel. Find or create an appropriate Forwarder.
+ ForwarderMap::iterator
+ entry(mForwarders.insert(ForwarderMap::value_type(channel, ForwarderMap::mapped_type())).first);
+ if (! entry->second)
+ {
+ entry->second.reset(new Forwarder(mNotifications, channel));
+ }
+ // Now, whether this Forwarder is brand-new or not, update it with the new
+ // request info.
+ Forwarder& fwd(*entry->second);
+ fwd.setPumpName(params["pump"]);
+ fwd.setTypes(params["types"]);
+ fwd.setRespond(params["respond"]);
+}
+
+bool LLNotificationsListener::Forwarder::handle(const LLSD& notification) const
+{
+ LL_INFOS("LLNotificationsListener") << "handle(" << notification << ")" << LL_ENDL;
+ if (notification["sigtype"].asString() == "delete")
+ {
+ LL_INFOS("LLNotificationsListener") << "ignoring delete" << LL_ENDL;
+ // let other listeners see the "delete" operation
+ return false;
+ }
+ LLNotificationPtr note(mNotifications.find(notification["id"]));
+ if (! note)
+ {
+ LL_INFOS("LLNotificationsListener") << notification["id"] << " not found" << LL_ENDL;
+ return false;
+ }
+ if (! matchType(mTypes, note->getType()))
+ {
+ LL_INFOS("LLNotificationsListener") << "didn't match types " << mTypes << LL_ENDL;
+ // We're not supposed to intercept this particular notification. Let
+ // other listeners process it.
+ return false;
+ }
+ LL_INFOS("LLNotificationsListener") << "sending via '" << mPumpName << "'" << LL_ENDL;
+ // This is a notification we care about. Forward it through specified
+ // LLEventPump.
+ LLEventPumps::instance().obtain(mPumpName).post(asLLSD(note));
+ // Are we also being asked to auto-respond?
+ if (mRespond)
+ {
+ LL_INFOS("LLNotificationsListener") << "should respond" << LL_ENDL;
+ note->respond(LLSD::emptyMap());
+ // Did that succeed in removing the notification? Only cancel() if
+ // it's still around -- otherwise we get an LL_ERRS crash!
+ note = mNotifications.find(notification["id"]);
+ if (note)
+ {
+ LL_INFOS("LLNotificationsListener") << "respond() didn't clear, canceling" << LL_ENDL;
+ mNotifications.cancel(note);
+ }
+ }
+ // If we've auto-responded to this notification, then it's going to be
+ // deleted. Other listeners would get the change operation, try to look it
+ // up and be baffled by lookup failure. So when we auto-respond, suppress
+ // this notification: don't pass it to other listeners.
+ return mRespond;
+}
+
+bool LLNotificationsListener::Forwarder::matchType(const LLSD& filter, const std::string& type) const
+{
+ // Decide whether this notification matches filter:
+ // undefined: forward all notifications
+ if (filter.isUndefined())
+ {
+ return true;
+ }
+ // array of string: forward any notification matching any named type
+ if (filter.isArray())
+ {
+ for (LLSD::array_const_iterator ti(filter.beginArray()), tend(filter.endArray());
+ ti != tend; ++ti)
+ {
+ if (ti->asString() == type)
+ {
+ return true;
+ }
+ }
+ // Didn't match any entry in the array
+ return false;
+ }
+ // string: forward only the specific named type
+ return (filter.asString() == type);
+}
+
+LLSD LLNotificationsListener::asLLSD(LLNotificationPtr note)
+{
+ LLSD notificationInfo(note->asLLSD());
+ // For some reason the following aren't included in LLNotification::asLLSD().
+ notificationInfo["summary"] = note->summarize();
+ notificationInfo["id"] = note->id();
+ notificationInfo["type"] = note->getType();
+ notificationInfo["message"] = note->getMessage();
+ notificationInfo["label"] = note->getLabel();
+ return notificationInfo;
+}
diff --git a/indra/llui/llnotificationslistener.h b/indra/llui/llnotificationslistener.h
new file mode 100644
index 0000000000..f9f7641de6
--- /dev/null
+++ b/indra/llui/llnotificationslistener.h
@@ -0,0 +1,69 @@
+/**
+ * @file llnotificationslistener.h
+ * @author Brad Kittenbrink
+ * @date 2009-07-08
+ * @brief Wrap subset of LLNotifications API in event API for test scripts.
+ *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLNOTIFICATIONSLISTENER_H
+#define LL_LLNOTIFICATIONSLISTENER_H
+
+#include "lleventapi.h"
+#include "llnotificationptr.h"
+#include <boost/shared_ptr.hpp>
+#include <map>
+#include <string>
+
+class LLNotifications;
+class LLSD;
+
+class LLNotificationsListener : public LLEventAPI
+{
+public:
+ LLNotificationsListener(LLNotifications & notifications);
+ ~LLNotificationsListener();
+
+private:
+ void requestAdd(LLSD const & event_data) const;
+
+ void NotificationResponder(const std::string& replypump,
+ const LLSD& notification,
+ const LLSD& response) const;
+
+ void listChannels(const LLSD& params) const;
+ void listChannelNotifications(const LLSD& params) const;
+ void respond(const LLSD& params) const;
+ void cancel(const LLSD& params) const;
+ void ignore(const LLSD& params) const;
+ void forward(const LLSD& params);
+
+ static LLSD asLLSD(LLNotificationPtr);
+
+ class Forwarder;
+ typedef std::map<std::string, boost::shared_ptr<Forwarder> > ForwarderMap;
+ ForwarderMap mForwarders;
+ LLNotifications & mNotifications;
+};
+
+#endif // LL_LLNOTIFICATIONSLISTENER_H
diff --git a/indra/llui/llnotificationsutil.cpp b/indra/llui/llnotificationsutil.cpp
new file mode 100644
index 0000000000..cc791c26d1
--- /dev/null
+++ b/indra/llui/llnotificationsutil.cpp
@@ -0,0 +1,95 @@
+/**
+ * @file llnotificationsutil.cpp
+ *
+ * $LicenseInfo:firstyear=2008&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+#include "linden_common.h"
+
+#include "llnotificationsutil.h"
+
+#include "llnotifications.h"
+#include "llsd.h"
+#include "llxmlnode.h" // apparently needed to call LLNotifications::instance()
+
+LLNotificationPtr LLNotificationsUtil::add(const std::string& name)
+{
+ LLNotification::Params::Functor functor_p;
+ functor_p.name = name;
+ return LLNotifications::instance().add(
+ LLNotification::Params().name(name).substitutions(LLSD()).payload(LLSD()).functor(functor_p));
+}
+
+LLNotificationPtr LLNotificationsUtil::add(const std::string& name,
+ const LLSD& substitutions)
+{
+ LLNotification::Params::Functor functor_p;
+ functor_p.name = name;
+ return LLNotifications::instance().add(
+ LLNotification::Params().name(name).substitutions(substitutions).payload(LLSD()).functor(functor_p));
+}
+
+LLNotificationPtr LLNotificationsUtil::add(const std::string& name,
+ const LLSD& substitutions,
+ const LLSD& payload)
+{
+ LLNotification::Params::Functor functor_p;
+ functor_p.name = name;
+ return LLNotifications::instance().add(
+ LLNotification::Params().name(name).substitutions(substitutions).payload(payload).functor(functor_p));
+}
+
+LLNotificationPtr LLNotificationsUtil::add(const std::string& name,
+ const LLSD& substitutions,
+ const LLSD& payload,
+ const std::string& functor_name)
+{
+ LLNotification::Params::Functor functor_p;
+ functor_p.name = functor_name;
+ return LLNotifications::instance().add(
+ LLNotification::Params().name(name).substitutions(substitutions).payload(payload).functor(functor_p));
+}
+
+LLNotificationPtr LLNotificationsUtil::add(const std::string& name,
+ const LLSD& substitutions,
+ const LLSD& payload,
+ boost::function<void (const LLSD&, const LLSD&)> functor)
+{
+ LLNotification::Params::Functor functor_p;
+ functor_p.function = functor;
+ return LLNotifications::instance().add(
+ LLNotification::Params().name(name).substitutions(substitutions).payload(payload).functor(functor_p));
+}
+
+S32 LLNotificationsUtil::getSelectedOption(const LLSD& notification, const LLSD& response)
+{
+ return LLNotification::getSelectedOption(notification, response);
+}
+
+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
new file mode 100644
index 0000000000..4093324d0c
--- /dev/null
+++ b/indra/llui/llnotificationsutil.h
@@ -0,0 +1,66 @@
+/**
+ * @file llnotificationsutil.h
+ *
+ * $LicenseInfo:firstyear=2008&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+#ifndef LLNOTIFICATIONSUTIL_H
+#define LLNOTIFICATIONSUTIL_H
+
+// The vast majority of clients of the notifications system just want to add
+// a notification to the screen, so define this lightweight public interface
+// to avoid including the heavyweight llnotifications.h
+
+#include "llnotificationptr.h"
+
+#include <boost/function.hpp>
+
+class LLSD;
+
+namespace LLNotificationsUtil
+{
+ LLNotificationPtr add(const std::string& name);
+
+ LLNotificationPtr add(const std::string& name,
+ const LLSD& substitutions);
+
+ LLNotificationPtr add(const std::string& name,
+ const LLSD& substitutions,
+ const LLSD& payload);
+
+ LLNotificationPtr add(const std::string& name,
+ const LLSD& substitutions,
+ const LLSD& payload,
+ const std::string& functor_name);
+
+ LLNotificationPtr add(const std::string& name,
+ const LLSD& substitutions,
+ const LLSD& payload,
+ boost::function<void (const LLSD&, const LLSD&)> functor);
+
+ 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 ad5cdca5cc..b2e08c48c5 100644
--- a/indra/llui/llpanel.cpp
+++ b/indra/llui/llpanel.cpp
@@ -2,31 +2,25 @@
* @file llpanel.cpp
* @brief LLPanel base class
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -34,15 +28,17 @@
#include "linden_common.h"
+#define LLPANEL_CPP
#include "llpanel.h"
-#include "llalertdialog.h"
#include "llfocusmgr.h"
#include "llfontgl.h"
#include "llrect.h"
#include "llerror.h"
#include "lltimer.h"
+#include "llaccordionctrltab.h"
+#include "llbutton.h"
#include "llmenugl.h"
//#include "llstatusbar.h"
#include "llui.h"
@@ -53,22 +49,42 @@
#include "lluictrl.h"
#include "lluictrlfactory.h"
#include "llviewborder.h"
-#include "llbutton.h"
#include "lltabcontainer.h"
-static LLDefaultWidgetRegistry::Register<LLPanel> r1("panel", &LLPanel::fromXML);
+static LLDefaultChildRegistry::Register<LLPanel> r1("panel", &LLPanel::fromXML);
+
+// Compiler optimization, generate extern template
+template class LLPanel* LLView::getChild<class LLPanel>(
+ const std::string& name, BOOL recurse) const;
+
+LLPanel::LocalizedString::LocalizedString()
+: name("name"),
+ value("value")
+{}
+
+const LLPanel::Params& LLPanel::getDefaultParams()
+{
+ return LLUICtrlFactory::getDefaultParams<LLPanel>();
+}
LLPanel::Params::Params()
: has_border("border", false),
- bg_opaque_color("bg_opaque_color"),
- bg_alpha_color("bg_alpha_color"),
+ border(""),
background_visible("background_visible", false),
background_opaque("background_opaque", false),
+ bg_opaque_color("bg_opaque_color"),
+ bg_alpha_color("bg_alpha_color"),
+ bg_opaque_image_overlay("bg_opaque_image_overlay"),
+ bg_alpha_image_overlay("bg_alpha_image_overlay"),
+ bg_opaque_image("bg_opaque_image"),
+ bg_alpha_image("bg_alpha_image"),
min_width("min_width", 100),
min_height("min_height", 100),
strings("string"),
filename("filename"),
- class_name("class")
+ class_name("class"),
+ help_topic("help_topic"),
+ visible_callback("visible_callback")
{
name = "panel";
addSynonym(background_visible, "bg_visible");
@@ -79,18 +95,25 @@ LLPanel::Params::Params()
LLPanel::LLPanel(const LLPanel::Params& p)
: LLUICtrl(p),
- mBgColorAlpha(p.bg_alpha_color().get()),
- mBgColorOpaque(p.bg_opaque_color().get()),
mBgVisible(p.background_visible),
mBgOpaque(p.background_opaque),
+ mBgOpaqueColor(p.bg_opaque_color()),
+ mBgAlphaColor(p.bg_alpha_color()),
+ mBgOpaqueImageOverlay(p.bg_opaque_image_overlay),
+ mBgAlphaImageOverlay(p.bg_alpha_image_overlay),
+ mBgOpaqueImage(p.bg_opaque_image()),
+ mBgAlphaImage(p.bg_alpha_image()),
mDefaultBtn(NULL),
mBorder(NULL),
mLabel(p.label),
+ mHelpTopic(p.help_topic),
mCommitCallbackRegistrar(false),
- mEnableCallbackRegistrar(false)
+ mEnableCallbackRegistrar(false),
+ mXMLFilename(p.filename),
+ mVisibleSignal(NULL)
+ // *NOTE: Be sure to also change LLPanel::initFromParams(). We have too
+ // many classes derived from LLPanel to retrofit them all to pass in params.
{
- setIsChrome(FALSE);
-
if (p.has_border)
{
addBorder(p.border);
@@ -99,6 +122,11 @@ LLPanel::LLPanel(const LLPanel::Params& p)
mPanelHandle.bind(this);
}
+LLPanel::~LLPanel()
+{
+ delete mVisibleSignal;
+}
+
// virtual
BOOL LLPanel::isPanel() const
{
@@ -114,6 +142,14 @@ void LLPanel::addBorder(LLViewBorder::Params p)
addChild( mBorder );
}
+void LLPanel::addBorder()
+{
+ LLViewBorder::Params p;
+ p.border_thickness(LLPANEL_BORDER_WIDTH);
+ addBorder(p);
+}
+
+
void LLPanel::removeBorder()
{
if (mBorder)
@@ -150,22 +186,36 @@ void LLPanel::setCtrlsEnabled( BOOL b )
void LLPanel::draw()
{
+ F32 alpha = getDrawContext().mAlpha;
+
// draw background
if( mBgVisible )
{
- //RN: I don't see the point of this
- S32 left = 0;//LLPANEL_BORDER_WIDTH;
- S32 top = getRect().getHeight();// - LLPANEL_BORDER_WIDTH;
- S32 right = getRect().getWidth();// - LLPANEL_BORDER_WIDTH;
- S32 bottom = 0;//LLPANEL_BORDER_WIDTH;
-
+ LLRect local_rect = getLocalRect();
if (mBgOpaque )
{
- gl_rect_2d( left, top, right, bottom, mBgColorOpaque );
+ // opaque, in-front look
+ if (mBgOpaqueImage.notNull())
+ {
+ mBgOpaqueImage->draw( local_rect, mBgOpaqueImageOverlay % alpha );
+ }
+ else
+ {
+ // fallback to flat colors when there are no images
+ gl_rect_2d( local_rect, mBgOpaqueColor.get() % alpha);
+ }
}
else
{
- gl_rect_2d( left, top, right, bottom, mBgColorAlpha );
+ // transparent, in-back look
+ if (mBgAlphaImage.notNull())
+ {
+ mBgAlphaImage->draw( local_rect, mBgAlphaImageOverlay % alpha );
+ }
+ else
+ {
+ gl_rect_2d( local_rect, mBgAlphaColor.get() % alpha );
+ }
}
}
@@ -176,16 +226,11 @@ void LLPanel::draw()
void LLPanel::updateDefaultBtn()
{
- // This method does not call LLView::draw() so callers will need
- // to take care of that themselves at the appropriate place in
- // their rendering sequence
-
if( mDefaultBtn)
{
if (gFocusMgr.childHasKeyboardFocus( this ) && mDefaultBtn->getEnabled())
{
- LLUICtrl* focus_ctrl = gFocusMgr.getKeyboardFocus();
- LLButton* buttonp = dynamic_cast<LLButton*>(focus_ctrl);
+ LLButton* buttonp = dynamic_cast<LLButton*>(gFocusMgr.getKeyboardFocus());
BOOL focus_is_child_button = buttonp && buttonp->getCommitOnReturn();
// only enable default button when current focus is not a return-capturing button
mDefaultBtn->setBorderEnabled(!focus_is_child_button);
@@ -233,12 +278,12 @@ BOOL LLPanel::handleKeyHere( KEY key, MASK mask )
{
BOOL handled = FALSE;
- LLUICtrl* cur_focus = gFocusMgr.getKeyboardFocus();
+ LLUICtrl* cur_focus = dynamic_cast<LLUICtrl*>(gFocusMgr.getKeyboardFocus());
// handle user hitting ESC to defocus
if (key == KEY_ESCAPE)
{
- gFocusMgr.setKeyboardFocus(NULL);
+ setFocus(FALSE);
return TRUE;
}
else if( (mask == MASK_SHIFT) && (KEY_TAB == key))
@@ -293,53 +338,25 @@ BOOL LLPanel::handleKeyHere( KEY key, MASK mask )
return handled;
}
-BOOL LLPanel::checkRequirements()
+void LLPanel::handleVisibilityChange ( BOOL new_visibility )
{
- if (!mRequirementsError.empty())
- {
- LLSD args;
- args["COMPONENTS"] = mRequirementsError;
- args["FLOATER"] = getName();
-
- llwarns << getName() << " failed requirements check on: \n"
- << mRequirementsError << llendl;
-
- LLNotifications::instance().add(LLNotification::Params("FailedRequirementsCheck").payload(args));
- mRequirementsError.clear();
- return FALSE;
- }
-
- return TRUE;
+ LLUICtrl::handleVisibilityChange ( new_visibility );
+ if (mVisibleSignal)
+ (*mVisibleSignal)(this, LLSD(new_visibility) ); // Pass BOOL as LLSD
}
void LLPanel::setFocus(BOOL b)
{
- if( b )
+ if( b && !hasFocus())
{
- if (!gFocusMgr.childHasKeyboardFocus(this))
- {
- // give ourselves focus preemptively, to avoid infinite loop
- LLUICtrl::setFocus(TRUE);
- // then try to pass to first valid child
- focusFirstItem();
- }
+ // give ourselves focus preemptively, to avoid infinite loop
+ LLUICtrl::setFocus(TRUE);
+ // then try to pass to first valid child
+ focusFirstItem();
}
else
{
- if( this == gFocusMgr.getKeyboardFocus() )
- {
- gFocusMgr.setKeyboardFocus( NULL );
- }
- else
- {
- //RN: why is this here?
- LLView::ctrl_list_t ctrls = getCtrlList();
- for (LLView::ctrl_list_t::iterator ctrl_it = ctrls.begin(); ctrl_it != ctrls.end(); ++ctrl_it)
- {
- LLUICtrl* ctrl = *ctrl_it;
- ctrl->setFocus( FALSE );
- }
- }
+ LLUICtrl::setFocus(b);
}
}
@@ -378,6 +395,12 @@ LLView* LLPanel::fromXML(LLXMLNodePtr node, LLView* parent, LLXMLNodePtr output_
if (!panelp)
{
panelp = LLUICtrlFactory::getInstance()->createFactoryPanel(name);
+ llassert(panelp);
+
+ if (!panelp)
+ {
+ return NULL; // :(
+ }
}
}
@@ -395,7 +418,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();
}
@@ -405,26 +428,36 @@ LLView* LLPanel::fromXML(LLXMLNodePtr node, LLView* parent, LLXMLNodePtr output_
void LLPanel::initFromParams(const LLPanel::Params& p)
{
+ //setting these here since panel constructor not called with params
+ //and LLView::initFromParams will use them to set visible and enabled
+ setVisible(p.visible);
+ setEnabled(p.enabled);
+
+ setSoundFlags(p.sound_flags);
+
// control_name, tab_stop, focus_lost_callback, initial_value, rect, enabled, visible
LLUICtrl::initFromParams(p);
-
+
+ // visible callback
+ if (p.visible_callback.isProvided())
+ {
+ setVisibleCallback(initCommitCallback(p.visible_callback));
+ }
+
for (LLInitParam::ParamIterator<LocalizedString>::const_iterator it = p.strings().begin();
it != p.strings().end();
++it)
{
- mUIStrings[it->name] = it->text;
+ mUIStrings[it->name] = it->value;
}
- setName(p.name());
setLabel(p.label());
-
+ setHelpTopic(p.help_topic);
setShape(p.rect);
parseFollowsFlags(p);
- setEnabled(p.enabled);
- setVisible(p.visible);
setToolTip(p.tool_tip());
- setSaveToXML(p.serializable);
+ setFromXUI(p.from_xui);
mHoverCursor = getCursorFromString(p.hover_cursor);
@@ -444,7 +477,10 @@ void LLPanel::initFromParams(const LLPanel::Params& p)
setBackgroundOpaque(p.background_opaque);
setBackgroundColor(p.bg_opaque_color().get());
setTransparentColor(p.bg_alpha_color().get());
-
+ mBgOpaqueImage = p.bg_opaque_image();
+ mBgAlphaImage = p.bg_alpha_image();
+ mBgOpaqueImageOverlay = p.bg_opaque_image_overlay;
+ mBgAlphaImageOverlay = p.bg_alpha_image_overlay;
}
static LLFastTimer::DeclareTimer FTM_PANEL_SETUP("Panel Setup");
@@ -453,24 +489,32 @@ static LLFastTimer::DeclareTimer FTM_PANEL_POSTBUILD("Panel PostBuild");
BOOL LLPanel::initPanelXML(LLXMLNodePtr node, LLView *parent, LLXMLNodePtr output_node)
{
- const LLPanel::Params& default_params(LLUICtrlFactory::getDefaultParams<LLPanel::Params>());
+ const LLPanel::Params& default_params(LLUICtrlFactory::getDefaultParams<LLPanel>());
Params params(default_params);
{
LLFastTimer timer(FTM_PANEL_SETUP);
LLXMLNodePtr referenced_xml;
- std::string xml_filename;
- node->getAttributeString("filename", xml_filename);
+ std::string xml_filename = mXMLFilename;
+
+ // if the panel didn't provide a filename, check the node
+ if (xml_filename.empty())
+ {
+ node->getAttributeString("filename", xml_filename);
+ setXMLFilename(xml_filename);
+ }
if (!xml_filename.empty())
{
+ LLUICtrlFactory::instance().pushFileName(xml_filename);
+
LLFastTimer timer(FTM_EXTERNAL_PANEL_LOAD);
if (output_node)
{
//if we are exporting, we want to export the current xml
//not the referenced xml
- LLXUIParser::instance().readXUI(node, params);
+ LLXUIParser::instance().readXUI(node, params, LLUICtrlFactory::getInstance()->getCurFileName());
Params output_params(params);
setupParamsForExport(output_params, parent);
output_node->setName(node->getName()->mString);
@@ -486,14 +530,17 @@ BOOL LLPanel::initPanelXML(LLXMLNodePtr node, LLView *parent, LLXMLNodePtr outpu
return FALSE;
}
- LLXUIParser::instance().readXUI(referenced_xml, params);
+ LLXUIParser::instance().readXUI(referenced_xml, params, LLUICtrlFactory::getInstance()->getCurFileName());
// add children using dimensions from referenced xml for consistent layout
setShape(params.rect);
- LLUICtrlFactory::createChildren(this, referenced_xml);
+ LLUICtrlFactory::createChildren(this, referenced_xml, child_registry_t::instance());
+
+ LLUICtrlFactory::instance().popFileName();
}
- LLXUIParser::instance().readXUI(node, params);
+ // ask LLUICtrlFactory for filename, since xml_filename might be empty
+ LLXUIParser::instance().readXUI(node, params, LLUICtrlFactory::getInstance()->getCurFileName());
if (output_node)
{
@@ -504,21 +551,22 @@ BOOL LLPanel::initPanelXML(LLXMLNodePtr node, LLView *parent, LLXMLNodePtr outpu
output_node, output_params, &default_params);
}
- setupParams(params, parent);
+ params.from_xui = true;
+ applyXUILayout(params, parent);
{
LLFastTimer timer(FTM_PANEL_CONSTRUCTION);
initFromParams(params);
}
// add children
- LLUICtrlFactory::createChildren(this, node, output_node);
+ LLUICtrlFactory::createChildren(this, node, child_registry_t::instance(), output_node);
// Connect to parent after children are built, because tab containers
// do a reshape() on their child panels, which requires that the children
// be built/added. JC
if (parent)
{
- S32 tab_group = params.tab_group.isProvided() ? params.tab_group() : -1;
+ S32 tab_group = params.tab_group.isProvided() ? params.tab_group() : parent->getLastTabGroup();
parent->addChild(this, tab_group);
}
@@ -530,12 +578,6 @@ BOOL LLPanel::initPanelXML(LLXMLNodePtr node, LLView *parent, LLXMLNodePtr outpu
return TRUE;
}
-const widget_registry_t& LLPanel::getChildRegistry() const
-{
- // use default widget registry
- return LLDefaultWidgetRegistry::instance();
-}
-
bool LLPanel::hasString(const std::string& name)
{
return mUIStrings.find(name) != mUIStrings.end();
@@ -613,7 +655,7 @@ void LLPanel::childSetEnabled(const std::string& id, bool enabled)
void LLPanel::childSetTentative(const std::string& id, bool tentative)
{
- LLView* child = findChild<LLView>(id);
+ LLUICtrl* child = findChild<LLUICtrl>(id);
if (child)
{
child->setTentative(tentative);
@@ -678,12 +720,14 @@ BOOL LLPanel::childHasFocus(const std::string& id)
}
else
{
- childNotFound(id);
return FALSE;
}
}
// *TODO: Deprecate; for backwards compatability only:
+// Prefer getChild<LLUICtrl>("foo")->setCommitCallback(boost:bind(...)),
+// which takes a generic slot. Or use mCommitCallbackRegistrar.add() with
+// a named callback and reference it in XML.
void LLPanel::childSetCommitCallback(const std::string& id, boost::function<void (LLUICtrl*,void*)> cb, void* data)
{
LLUICtrl* child = findChild<LLUICtrl>(id);
@@ -791,24 +835,6 @@ BOOL LLPanel::childSetToolTipArg(const std::string& id, const std::string& key,
return FALSE;
}
-void LLPanel::childSetMinValue(const std::string& id, LLSD min_value)
-{
- LLUICtrl* child = findChild<LLUICtrl>(id);
- if (child)
- {
- child->setMinValue(min_value);
- }
-}
-
-void LLPanel::childSetMaxValue(const std::string& id, LLSD max_value)
-{
- LLUICtrl* child = findChild<LLUICtrl>(id);
- if (child)
- {
- child->setMaxValue(max_value);
- }
-}
-
void LLPanel::childShowTab(const std::string& id, const std::string& tabname, bool visible)
{
LLTabContainer* child = findChild<LLTabContainer>(id);
@@ -828,22 +854,72 @@ LLPanel *LLPanel::childGetVisibleTab(const std::string& id) const
return NULL;
}
-void LLPanel::childSetPrevalidate(const std::string& id, BOOL (*func)(const LLWString &) )
+LLPanel* LLPanel::childGetVisibleTabWithHelp()
{
- LLLineEditor* child = findChild<LLLineEditor>(id);
- if (child)
+ LLView *child;
+
+ bfs_tree_iterator_t it = beginTreeBFS();
+ // skip ourselves
+ ++it;
+ for (; it != endTreeBFS(); ++it)
{
- child->setPrevalidate(func);
+ child = *it;
+ LLPanel *curTabPanel = NULL;
+
+ // do we have a tab container?
+ LLTabContainer *tab = dynamic_cast<LLTabContainer *>(child);
+ if (tab && tab->getVisible())
+ {
+ curTabPanel = tab->getCurrentPanel();
+ }
+
+ // do we have an accordion tab?
+ LLAccordionCtrlTab* accordion = dynamic_cast<LLAccordionCtrlTab *>(child);
+ if (accordion && accordion->getDisplayChildren())
+ {
+ curTabPanel = dynamic_cast<LLPanel *>(accordion->getAccordionView());
+ }
+
+ // if we found a valid tab, does it have a help topic?
+ if (curTabPanel && !curTabPanel->getHelpTopic().empty())
+ {
+ return curTabPanel;
+ }
}
+
+ // couldn't find any active tabs with a help topic string
+ return NULL;
}
-void LLPanel::childSetWrappedText(const std::string& id, const std::string& text, bool visible)
+
+LLPanel *LLPanel::childGetVisiblePanelWithHelp()
{
- LLTextBox* child = findChild<LLTextBox>(id);
- if (child)
+ LLView *child;
+
+ bfs_tree_iterator_t it = beginTreeBFS();
+ // skip ourselves
+ ++it;
+ for (; it != endTreeBFS(); ++it)
{
- child->setVisible(visible);
- child->setWrappedText(text);
+ child = *it;
+ // do we have a panel with a help topic?
+ LLPanel *panel = dynamic_cast<LLPanel *>(child);
+ if (panel && panel->getVisible() && !panel->getHelpTopic().empty())
+ {
+ return panel;
+ }
+ }
+
+ // couldn't find any active panels with a help topic string
+ return NULL;
+}
+
+void LLPanel::childSetAction(const std::string& id, const commit_signal_t::slot_type& function)
+{
+ LLButton* button = findChild<LLButton>(id);
+ if (button)
+ {
+ button->setClickedCallback(function);
}
}
@@ -856,12 +932,12 @@ void LLPanel::childSetAction(const std::string& id, boost::function<void(void*)>
}
}
-void LLPanel::childSetActionTextbox(const std::string& id, void(*function)(void*), void* value)
+void LLPanel::childSetActionTextbox(const std::string& id, boost::function<void(void*)> function, void* value)
{
LLTextBox* textbox = findChild<LLTextBox>(id);
if (textbox)
{
- textbox->setClickedCallback(function, value);
+ textbox->setClickedCallback(boost::bind(function, value));
}
}
@@ -874,56 +950,12 @@ void LLPanel::childSetControlName(const std::string& id, const std::string& cont
}
}
-//virtual
-LLView* LLPanel::getChildView(const std::string& name, BOOL recurse, BOOL create_if_missing) const
+boost::signals2::connection LLPanel::setVisibleCallback( const commit_signal_t::slot_type& cb )
{
- // just get child, don't try to create a dummy one
- LLView* view = LLUICtrl::getChildView(name, recurse, FALSE);
- if (!view && !recurse)
+ if (!mVisibleSignal)
{
- childNotFound(name);
+ mVisibleSignal = new commit_signal_t();
}
- if (!view && create_if_missing)
- {
- view = getDummyWidget<LLView>(name);
- if (!view)
- {
- view = LLUICtrlFactory::createDummyWidget<LLView>(name);
- }
- }
- return view;
-}
-
-void LLPanel::childNotFound(const std::string& id) const
-{
- if (mExpectedMembers.find(id) == mExpectedMembers.end())
- {
- mNewExpectedMembers.insert(id);
- }
-}
-
-void LLPanel::childDisplayNotFound()
-{
- if (mNewExpectedMembers.empty())
- {
- return;
- }
- std::string msg;
- expected_members_list_t::iterator itor;
- for (itor=mNewExpectedMembers.begin(); itor!=mNewExpectedMembers.end(); ++itor)
- {
- msg.append(*itor);
- msg.append("\n");
- mExpectedMembers.insert(*itor);
- }
- mNewExpectedMembers.clear();
- LLSD args;
- args["CONTROLS"] = msg;
- LLNotifications::instance().add("FloaterNotFound", args);
-}
-void LLPanel::requires(const std::string& name)
-{
- requires<LLView>(name);
+ return mVisibleSignal->connect(cb);
}
-
diff --git a/indra/llui/llpanel.h b/indra/llui/llpanel.h
index b3ccdd0f00..a7224648c1 100644
--- a/indra/llui/llpanel.h
+++ b/indra/llui/llpanel.h
@@ -3,31 +3,25 @@
* @author James Cook, Tom Yedwab
* @brief LLPanel base class
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -37,8 +31,6 @@
#include "llcallbackmap.h"
#include "lluictrl.h"
-#include "llbutton.h"
-#include "lllineeditor.h"
#include "llviewborder.h"
#include "lluistring.h"
#include "v4color.h"
@@ -49,6 +41,8 @@ const S32 LLPANEL_BORDER_WIDTH = 1;
const BOOL BORDER_YES = TRUE;
const BOOL BORDER_NO = FALSE;
+class LLButton;
+class LLUIImage;
/*
* General purpose concrete view base class.
@@ -62,12 +56,9 @@ public:
struct LocalizedString : public LLInitParam::Block<LocalizedString>
{
Mandatory<std::string> name;
- Mandatory<std::string> text;
+ Mandatory<std::string> value;
- LocalizedString()
- : name("name"),
- text("value")
- {}
+ LocalizedString();
};
struct Params
@@ -76,42 +67,51 @@ public:
Optional<bool> has_border;
Optional<LLViewBorder::Params> border;
- Optional<LLUIColor> bg_opaque_color,
- bg_alpha_color;
-
Optional<bool> background_visible,
background_opaque;
+ Optional<LLUIColor> bg_opaque_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;
+
Optional<S32> min_width,
min_height;
Optional<std::string> filename;
Optional<std::string> class_name;
+ Optional<std::string> help_topic;
Multiple<LocalizedString> strings;
-
+
+ Optional<CommitCallbackParam> visible_callback;
+
Params();
};
+ // valid children for LLPanel are stored in this registry
+ typedef LLDefaultChildRegistry child_registry_t;
+
protected:
friend class LLUICtrlFactory;
// RN: for some reason you can't just use LLUICtrlFactory::getDefaultParams as a default argument in VC8
- static const Params& defaultParams() { return LLUICtrlFactory::getDefaultParams<LLPanel::Params>(); }
+ static const LLPanel::Params& getDefaultParams();
// Panels can get constructed directly
- LLPanel(const Params& params = defaultParams());
+ LLPanel(const LLPanel::Params& params = getDefaultParams());
public:
// LLPanel(const std::string& name, const LLRect& rect = LLRect(), BOOL bordered = TRUE);
- /*virtual*/ ~LLPanel() {}
+ /*virtual*/ ~LLPanel();
// LLView interface
/*virtual*/ BOOL isPanel() const;
/*virtual*/ void draw();
/*virtual*/ BOOL handleKeyHere( KEY key, MASK mask );
-
- // Override to set not found list:
- /*virtual*/ LLView* getChildView(const std::string& name, BOOL recurse = TRUE, BOOL create_if_missing = TRUE) const;
+ /*virtual*/ void handleVisibilityChange ( BOOL new_visibility );
// From LLFocusableElement
/*virtual*/ void setFocus( BOOL b );
@@ -122,28 +122,19 @@ public:
// Border controls
void addBorder( LLViewBorder::Params p);
- void addBorder() { LLViewBorder::Params p; p.border_thickness(LLPANEL_BORDER_WIDTH); addBorder(p); }
+ void addBorder();
void removeBorder();
BOOL hasBorder() const { return mBorder != NULL; }
void setBorderVisible( BOOL b );
- template <class T> void requires(const std::string& name)
- {
- // check for widget with matching type and name
- if (LLView::getChild<T>(name) == NULL)
- {
- mRequirementsError += name + "\n";
- }
- }
-
- // requires LLView by default
- void requires(const std::string& name);
- BOOL checkRequirements();
-
- void setBackgroundColor( const LLColor4& color ) { mBgColorOpaque = color; }
- const LLColor4& getBackgroundColor() const { return mBgColorOpaque; }
- void setTransparentColor(const LLColor4& color) { mBgColorAlpha = color; }
- const LLColor4& getTransparentColor() const { return mBgColorAlpha; }
+ void setBackgroundColor( const LLColor4& color ) { mBgOpaqueColor = color; }
+ const LLColor4& getBackgroundColor() const { return mBgOpaqueColor; }
+ void setTransparentColor(const LLColor4& color) { mBgAlphaColor = color; }
+ const LLColor4& getTransparentColor() const { return mBgAlphaColor; }
+ 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; }
@@ -153,6 +144,8 @@ public:
void updateDefaultBtn();
void setLabel(const LLStringExplicit& label) { mLabel = label; }
std::string getLabel() const { return mLabel; }
+ void setHelpTopic(const std::string& help_topic) { mHelpTopic = help_topic; }
+ std::string getHelpTopic() const { return mHelpTopic; }
void setCtrlsEnabled(BOOL b);
@@ -165,13 +158,13 @@ public:
void initFromParams(const Params& p);
BOOL initPanelXML(LLXMLNodePtr node, LLView *parent, LLXMLNodePtr output_node = NULL);
- /*virtual*/ const widget_registry_t& getChildRegistry() const;
bool hasString(const std::string& name);
std::string getString(const std::string& name, const LLStringUtil::format_map_t& args) const;
std::string getString(const std::string& name) const;
// ** Wrappers for setting child properties by name ** -TomY
+ // WARNING: These are deprecated, please use getChild<T>("name")->doStuff() idiom instead
// LLView
void childSetVisible(const std::string& name, bool visible);
@@ -194,7 +187,11 @@ public:
BOOL childHasFocus(const std::string& id);
// *TODO: Deprecate; for backwards compatability only:
+ // Prefer getChild<LLUICtrl>("foo")->setCommitCallback(boost:bind(...)),
+ // which takes a generic slot. Or use mCommitCallbackRegistrar.add() with
+ // a named callback and reference it in XML.
void childSetCommitCallback(const std::string& id, boost::function<void (LLUICtrl*,void*)> cb, void* data);
+
void childSetValidate(const std::string& id, boost::function<bool (const LLSD& data)> cb );
void childSetColor(const std::string& id, const LLColor4& color);
@@ -213,16 +210,13 @@ public:
BOOL childSetLabelArg(const std::string& id, const std::string& key, const LLStringExplicit& text);
BOOL childSetToolTipArg(const std::string& id, const std::string& key, const LLStringExplicit& text);
- // LLSlider / LLMultiSlider / LLSpinCtrl
- void childSetMinValue(const std::string& id, LLSD min_value);
- void childSetMaxValue(const std::string& id, LLSD max_value);
-
// LLTabContainer
void childShowTab(const std::string& id, const std::string& tabname, bool visible = true);
LLPanel *childGetVisibleTab(const std::string& id) const;
- // LLTextBox
- void childSetWrappedText(const std::string& id, const std::string& text, bool visible = true);
+ // Find a child with a nonempty Help topic
+ LLPanel *childGetVisibleTabWithHelp();
+ LLPanel *childGetVisiblePanelWithHelp();
// LLTextBox/LLTextEditor/LLLineEditor
void childSetText(const std::string& id, const LLStringExplicit& text) { childSetValue(id, LLSD(text)); }
@@ -231,39 +225,47 @@ 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);
- void childSetActionTextbox(const std::string& id, void(*function)(void*), void* value = NULL);
- void childSetControlName(const std::string& id, const std::string& control_name);
+ void childSetAction(const std::string& id, boost::function<void(void*)> function, void* value);
+ void childSetAction(const std::string& id, const commit_signal_t::slot_type& function);
- // Error reporting
- void childNotFound(const std::string& id) const;
- void childDisplayNotFound();
+ // LLTextBox
+ void childSetActionTextbox(const std::string& id, boost::function<void(void*)> function, void* value = NULL);
+
+ void childSetControlName(const std::string& id, const std::string& control_name);
static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLXMLNodePtr output_node = NULL);
//call onOpen to let panel know when it's about to be shown or activated
virtual void onOpen(const LLSD& key) {}
+
+ void setXMLFilename(std::string filename) { mXMLFilename = filename; };
+ std::string getXMLFilename() { return mXMLFilename; };
+ boost::signals2::connection setVisibleCallback( const commit_signal_t::slot_type& cb );
+
protected:
// Override to set not found list
LLButton* getDefaultButton() { return mDefaultBtn; }
LLCallbackMap::map_t mFactoryMap;
CommitCallbackRegistry::ScopedRegistrar mCommitCallbackRegistrar;
EnableCallbackRegistry::ScopedRegistrar mEnableCallbackRegistrar;
+
+ commit_signal_t* mVisibleSignal; // Called when visibility changes, passes new visibility as LLSD()
+
+ std::string mHelpTopic; // the name of this panel's help topic to display in the Help Viewer
private:
- // Unified error reporting for the child* functions
- typedef std::set<std::string> expected_members_list_t;
- mutable expected_members_list_t mExpectedMembers;
- mutable expected_members_list_t mNewExpectedMembers;
-
- LLColor4 mBgColorAlpha;
- LLColor4 mBgColorOpaque;
- BOOL mBgVisible;
- BOOL mBgOpaque;
+ BOOL mBgVisible; // any background at all?
+ 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;
LLButton* mDefaultBtn;
LLUIString mLabel;
@@ -272,8 +274,15 @@ private:
typedef std::map<std::string, std::string> ui_string_map_t;
ui_string_map_t mUIStrings;
- std::string mRequirementsError;
+ // for setting the xml filename when building panel in context dependent cases
+ std::string mXMLFilename;
}; // end class LLPanel
+// Build time optimization, generate once in .cpp file
+#ifndef LLPANEL_CPP
+extern template class LLPanel* LLView::getChild<class LLPanel>(
+ const std::string& name, BOOL recurse) const;
+#endif
+
#endif
diff --git a/indra/llui/llprogressbar.cpp b/indra/llui/llprogressbar.cpp
index 779967940a..aaa328754d 100644
--- a/indra/llui/llprogressbar.cpp
+++ b/indra/llui/llprogressbar.cpp
@@ -2,31 +2,25 @@
* @file llprogressbar.cpp
* @brief LLProgressBar class implementation
*
- * $LicenseInfo:firstyear=2002&license=viewergpl$
- *
- * Copyright (c) 2002-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -39,22 +33,18 @@
#include "llgl.h"
#include "llui.h"
#include "llfontgl.h"
-#include "llimagegl.h"
#include "lltimer.h"
#include "llglheaders.h"
#include "llfocusmgr.h"
#include "lluictrlfactory.h"
-static LLDefaultWidgetRegistry::Register<LLProgressBar> r("progress_bar");
+static LLDefaultChildRegistry::Register<LLProgressBar> r("progress_bar");
LLProgressBar::Params::Params()
: image_bar("image_bar"),
image_fill("image_fill"),
- image_shadow("image_shadow"),
color_bar("color_bar"),
- color_bar2("color_bar2"),
- color_shadow("color_shadow"),
color_bg("color_bg")
{}
@@ -62,12 +52,9 @@ LLProgressBar::Params::Params()
LLProgressBar::LLProgressBar(const LLProgressBar::Params& p)
: LLView(p),
mImageBar(p.image_bar),
- mImageShadow(p.image_shadow),
mImageFill(p.image_fill),
mColorBackground(p.color_bg()),
mColorBar(p.color_bar()),
- mColorBar2(p.color_bar2()),
- mColorShadow(p.color_shadow()),
mPercentDone(0.f)
{}
@@ -79,17 +66,18 @@ LLProgressBar::~LLProgressBar()
void LLProgressBar::draw()
{
static LLTimer timer;
-
- LLUIImagePtr bar_fg_imagep = LLUI::getUIImage("progressbar_fill.tga");
+ F32 alpha = getDrawContext().mAlpha;
- mImageBar->draw(getLocalRect(), mColorBackground.get());
+ LLColor4 image_bar_color = mColorBackground.get();
+ image_bar_color.setAlpha(alpha);
+ mImageBar->draw(getLocalRect(), image_bar_color);
- F32 alpha = 0.5f + 0.5f*0.5f*(1.f + (F32)sin(3.f*timer.getElapsedTimeF32()));
+ alpha *= 0.5f + 0.5f*0.5f*(1.f + (F32)sin(3.f*timer.getElapsedTimeF32()));
LLColor4 bar_color = mColorBar.get();
- bar_color.mV[3] = alpha;
+ bar_color.mV[VALPHA] *= alpha; // modulate alpha
LLRect progress_rect = getLocalRect();
progress_rect.mRight = llround(getRect().getWidth() * (mPercentDone / 100.f));
- mImageFill->draw(progress_rect);
+ mImageFill->draw(progress_rect, bar_color);
}
void LLProgressBar::setPercent(const F32 percent)
diff --git a/indra/llui/llprogressbar.h b/indra/llui/llprogressbar.h
index 5c2f73ef9e..13297f7493 100644
--- a/indra/llui/llprogressbar.h
+++ b/indra/llui/llprogressbar.h
@@ -2,31 +2,25 @@
* @file llprogressbar.h
* @brief LLProgressBar class definition
*
- * $LicenseInfo:firstyear=2002&license=viewergpl$
- *
- * Copyright (c) 2002-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -43,12 +37,9 @@ public:
struct Params : public LLInitParam::Block<Params, LLView::Params>
{
Optional<LLUIImage*> image_bar,
- image_fill,
- image_shadow;
+ image_fill;
Optional<LLUIColor> color_bar,
- color_bar2,
- color_shadow,
color_bg;
Params();
@@ -65,10 +56,7 @@ private:
LLPointer<LLUIImage> mImageBar;
LLUIColor mColorBar;
- LLUIColor mColorBar2;
- LLPointer<LLUIImage> mImageShadow;
- LLUIColor mColorShadow;
LLUIColor mColorBackground;
LLPointer<LLUIImage> mImageFill;
diff --git a/indra/llui/llradiogroup.cpp b/indra/llui/llradiogroup.cpp
index 70f98bd908..83c42a5ab8 100644
--- a/indra/llui/llradiogroup.cpp
+++ b/indra/llui/llradiogroup.cpp
@@ -2,31 +2,25 @@
* @file llradiogroup.cpp
* @brief LLRadioGroup base class
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -43,22 +37,48 @@
#include "llui.h"
#include "llfocusmgr.h"
#include "lluictrlfactory.h"
+#include "llsdutil.h"
+
+static LLDefaultChildRegistry::Register<LLRadioGroup> r1("radio_group");
+
+/*
+ * An invisible view containing multiple mutually exclusive toggling
+ * buttons (usually radio buttons). Automatically handles the mutex
+ * condition by highlighting only one button at a time.
+ */
+class LLRadioCtrl : public LLCheckBoxCtrl
+{
+public:
+ typedef LLRadioGroup::ItemParams Params;
+ /*virtual*/ ~LLRadioCtrl();
+ /*virtual*/ void setValue(const LLSD& value);
-static LLDefaultWidgetRegistry::Register<LLRadioGroup> r1("radio_group");
+ /*virtual*/ BOOL postBuild();
-struct RadioGroupRegistry : public LLWidgetRegistry<RadioGroupRegistry>
-{};
+ LLSD getPayload() { return mPayload; }
-static RadioGroupRegistry::Register<LLRadioCtrl> register_radio_ctrl("radio_item");
+ // Ensure label is in an attribute, not the contents
+ static void setupParamsForExport(Params& p, LLView* parent);
+protected:
+ LLRadioCtrl(const LLRadioGroup::ItemParams& p);
+ friend class LLUICtrlFactory;
+ LLSD mPayload; // stores data that this item represents in the radio group
+};
+static LLWidgetNameRegistry::StaticRegistrar register_radio_item(&typeid(LLRadioGroup::ItemParams), "radio_item");
LLRadioGroup::Params::Params()
-: has_border("draw_border")
+: has_border("draw_border"),
+ items("item")
{
+ addSynonym(items, "radio_item");
+
name = "radio_group";
mouse_opaque = true;
follows.flags = FOLLOWS_LEFT | FOLLOWS_TOP;
+ // radio items are not tabbable until they are selected
+ tab_stop = false;
}
LLRadioGroup::LLRadioGroup(const LLRadioGroup::Params& p)
@@ -72,43 +92,51 @@ LLRadioGroup::LLRadioGroup(const LLRadioGroup::Params& p)
LLViewBorder::Params params;
params.name("radio group border");
params.rect(LLRect(0, getRect().getHeight(), getRect().getWidth(), 0));
- params.bevel_type(LLViewBorder::BEVEL_NONE);
+ params.bevel_style(LLViewBorder::BEVEL_NONE);
LLViewBorder * vb = LLUICtrlFactory::create<LLViewBorder> (params);
addChild (vb);
}
}
-LLRadioGroup::~LLRadioGroup()
+void LLRadioGroup::initFromParams(const Params& p)
{
+ for (LLInitParam::ParamIterator<ItemParams>::const_iterator it = p.items().begin();
+ it != p.items().end();
+ ++it)
+ {
+ LLRadioGroup::ItemParams item_params(*it);
+
+ item_params.font.setIfNotProvided(mFont); // apply radio group font by default
+ item_params.commit_callback.function = boost::bind(&LLRadioGroup::onClickButton, this, _1);
+ item_params.from_xui = p.from_xui;
+ if (p.from_xui)
+ {
+ applyXUILayout(item_params, this);
+ }
+
+ LLRadioCtrl* item = LLUICtrlFactory::create<LLRadioCtrl>(item_params, this);
+ mRadioButtons.push_back(item);
+ }
+
+ // call this *after* setting up mRadioButtons so we can handle setValue() calls
+ LLUICtrl::initFromParams(p);
}
-const widget_registry_t& LLRadioGroup::getChildRegistry() const
+
+LLRadioGroup::~LLRadioGroup()
{
- return RadioGroupRegistry::instance();
}
// virtual
BOOL LLRadioGroup::postBuild()
{
- if (mControlVariable)
+ if (!mRadioButtons.empty())
{
- setSelectedIndex(mControlVariable->getValue().asInteger());
+ mRadioButtons[0]->setTabStop(true);
}
return TRUE;
}
-// virtual
-void LLRadioGroup::setEnabled(BOOL enabled)
-{
- for (child_list_const_iter_t child_iter = getChildList()->begin();
- child_iter != getChildList()->end(); ++child_iter)
- {
- LLView *child = *child_iter;
- child->setEnabled(enabled);
- }
- LLView::setEnabled(enabled);
-}
-
void LLRadioGroup::setIndexEnabled(S32 index, BOOL enabled)
{
S32 count = 0;
@@ -156,16 +184,36 @@ void LLRadioGroup::setIndexEnabled(S32 index, BOOL enabled)
BOOL LLRadioGroup::setSelectedIndex(S32 index, BOOL from_event)
{
- if (index < 0 || index >= (S32)mRadioButtons.size())
+ if (index < 0 || (S32)mRadioButtons.size() <= index )
{
return FALSE;
}
+ if (mSelectedIndex >= 0)
+ {
+ LLRadioCtrl* old_radio_item = mRadioButtons[mSelectedIndex];
+ old_radio_item->setTabStop(false);
+ old_radio_item->setValue( FALSE );
+ }
+ else
+ {
+ mRadioButtons[0]->setTabStop(false);
+ }
+
mSelectedIndex = index;
+ LLRadioCtrl* radio_item = mRadioButtons[mSelectedIndex];
+ radio_item->setTabStop(true);
+ radio_item->setValue( TRUE );
+
+ if (hasFocus())
+ {
+ mRadioButtons[mSelectedIndex]->focusFirstItem(FALSE, FALSE);
+ }
+
if (!from_event)
{
- setControlValue(getSelectedIndex());
+ setControlValue(getValue());
}
return TRUE;
@@ -230,53 +278,18 @@ BOOL LLRadioGroup::handleKeyHere(KEY key, MASK mask)
return handled;
}
-void LLRadioGroup::draw()
+BOOL LLRadioGroup::handleMouseDown(S32 x, S32 y, MASK mask)
{
- S32 current_button = 0;
-
- BOOL take_focus = FALSE;
- if (gFocusMgr.childHasKeyboardFocus(this))
+ // grab focus preemptively, before child button takes mousecapture
+ //
+ if (hasTabStop())
{
- take_focus = TRUE;
+ focusFirstItem(FALSE, FALSE);
}
- for (button_list_t::iterator iter = mRadioButtons.begin();
- iter != mRadioButtons.end(); ++iter)
- {
- LLRadioCtrl* radio = *iter;
- BOOL selected = (current_button == mSelectedIndex);
- radio->setValue( selected );
- if (take_focus && selected && !gFocusMgr.childHasKeyboardFocus(radio))
- {
- // don't flash keyboard focus when navigating via keyboard
- BOOL DONT_FLASH = FALSE;
- radio->focusFirstItem(FALSE, DONT_FLASH);
- }
- current_button++;
- }
-
- LLView::draw();
+ return LLUICtrl::handleMouseDown(x, y, mask);
}
-// When adding a child button, we need to ensure that the radio
-// group gets a message when the button is clicked.
-
-/*virtual*/
-bool LLRadioGroup::addChild(LLView* view, S32 tab_group)
-{
- bool res = LLView::addChild(view, tab_group);
- if (res)
- {
- LLRadioCtrl* radio_ctrl = dynamic_cast<LLRadioCtrl*>(view);
- if (radio_ctrl)
- {
- radio_ctrl->setFont(mFont);
- radio_ctrl->setCommitCallback(boost::bind(&LLRadioGroup::onClickButton, this, _1));
- mRadioButtons.push_back(radio_ctrl);
- }
- }
- return res;
-}
// Handle one button being clicked. All child buttons must have this
// function as their callback function.
@@ -311,13 +324,12 @@ void LLRadioGroup::onClickButton(LLUICtrl* ctrl)
void LLRadioGroup::setValue( const LLSD& value )
{
- std::string value_name = value.asString();
int idx = 0;
for (button_list_t::const_iterator iter = mRadioButtons.begin();
iter != mRadioButtons.end(); ++iter)
{
LLRadioCtrl* radio = *iter;
- if (radio->getName() == value_name)
+ if (radio->getPayload().asString() == value.asString())
{
setSelectedIndex(idx);
idx = -1;
@@ -334,7 +346,7 @@ void LLRadioGroup::setValue( const LLSD& value )
}
else
{
- llwarns << "LLRadioGroup::setValue: value not found: " << value_name << llendl;
+ llwarns << "LLRadioGroup::setValue: value not found: " << value.asString() << llendl;
}
}
}
@@ -346,7 +358,7 @@ LLSD LLRadioGroup::getValue() const
for (button_list_t::const_iterator iter = mRadioButtons.begin();
iter != mRadioButtons.end(); ++iter)
{
- if (idx == index) return LLSD((*iter)->getName());
+ if (idx == index) return LLSD((*iter)->getPayload());
++idx;
}
return LLSD();
@@ -366,11 +378,10 @@ LLUUID LLRadioGroup::getCurrentID() const
BOOL LLRadioGroup::setSelectedByValue(const LLSD& value, BOOL selected)
{
S32 idx = 0;
- std::string value_string = value.asString();
for (button_list_t::const_iterator iter = mRadioButtons.begin();
iter != mRadioButtons.end(); ++iter)
{
- if((*iter)->getName() == value_string)
+ if((*iter)->getPayload().asString() == value.asString())
{
setSelectedIndex(idx);
return TRUE;
@@ -389,11 +400,10 @@ LLSD LLRadioGroup::getSelectedValue()
BOOL LLRadioGroup::isSelected(const LLSD& value) const
{
S32 idx = 0;
- std::string value_string = value.asString();
for (button_list_t::const_iterator iter = mRadioButtons.begin();
iter != mRadioButtons.end(); ++iter)
{
- if((*iter)->getName() == value_string)
+ if((*iter)->getPayload().asString() == value.asString())
{
if (idx == mSelectedIndex)
{
@@ -415,9 +425,21 @@ BOOL LLRadioGroup::operateOnAll(EOperation op)
return FALSE;
}
-LLRadioCtrl::LLRadioCtrl(const LLRadioCtrl::Params& p)
- : LLCheckBoxCtrl(p)
+LLRadioGroup::ItemParams::ItemParams()
+: value("value")
+{
+ addSynonym(value, "initial_value");
+}
+
+LLRadioCtrl::LLRadioCtrl(const LLRadioGroup::ItemParams& p)
+: LLCheckBoxCtrl(p),
+ mPayload(p.value)
{
+ // use name as default "Value" for backwards compatibility
+ if (!p.value.isProvided())
+ {
+ mPayload = p.name();
+ }
}
BOOL LLRadioCtrl::postBuild()
diff --git a/indra/llui/llradiogroup.h b/indra/llui/llradiogroup.h
index d3cb8a628e..0588900600 100644
--- a/indra/llui/llradiogroup.h
+++ b/indra/llui/llradiogroup.h
@@ -2,31 +2,25 @@
* @file llradiogroup.h
* @brief LLRadioGroup base class
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -37,39 +31,6 @@
#include "llcheckboxctrl.h"
#include "llctrlselectioninterface.h"
-
-/*
- * An invisible view containing multiple mutually exclusive toggling
- * buttons (usually radio buttons). Automatically handles the mutex
- * condition by highlighting only one button at a time.
- */
-class LLRadioCtrl : public LLCheckBoxCtrl
-{
-public:
- struct Params : public LLInitParam::Block<Params, LLCheckBoxCtrl::Params>
- {
- Deprecated length;
- Deprecated type;
-
- Params()
- : length("length"),
- type("type")
- {}
- };
-
- /*virtual*/ ~LLRadioCtrl();
- /*virtual*/ void setValue(const LLSD& value);
-
- /*virtual*/ BOOL postBuild();
-
- // Ensure label is in an attribute, not the contents
- static void setupParamsForExport(Params& p, LLView* parent);
-
-protected:
- LLRadioCtrl(const Params& p);
- friend class LLUICtrlFactory;
-};
-
/*
* An invisible view containing multiple mutually exclusive toggling
* buttons (usually radio buttons). Automatically handles the mutex
@@ -80,9 +41,16 @@ class LLRadioGroup
{
public:
+ struct ItemParams : public LLInitParam::Block<ItemParams, LLCheckBoxCtrl::Params>
+ {
+ Optional<LLSD> value;
+ ItemParams();
+ };
+
struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
{
- Optional<bool> has_border;
+ Optional<bool> has_border;
+ Multiple<ItemParams, AtLeast<1> > items;
Params();
};
@@ -91,17 +59,18 @@ protected:
friend class LLUICtrlFactory;
public:
+
+ /*virtual*/ void initFromParams(const Params&);
+
virtual ~LLRadioGroup();
virtual BOOL postBuild();
- virtual bool addChild(LLView* view, S32 tab_group = 0);
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
virtual BOOL handleKeyHere(KEY key, MASK mask);
- virtual void setEnabled(BOOL enabled);
void setIndexEnabled(S32 index, BOOL enabled);
-
// return the index value of the selected item
S32 getSelectedIndex() const { return mSelectedIndex; }
@@ -112,14 +81,9 @@ public:
virtual void setValue(const LLSD& value );
virtual LLSD getValue() const;
- // Draw the group, but also fix the highlighting based on the control.
- void draw();
-
// Update the control as needed. Userdata must be a pointer to the button.
void onClickButton(LLUICtrl* clicked_radio);
- virtual const widget_registry_t& getChildRegistry() const;
-
//========================================================================
LLCtrlSelectionInterface* getSelectionInterface() { return (LLCtrlSelectionInterface*)this; };
@@ -141,7 +105,7 @@ public:
private:
const LLFontGL* mFont;
S32 mSelectedIndex;
- typedef std::vector<LLRadioCtrl*> button_list_t;
+ typedef std::vector<class LLRadioCtrl*> button_list_t;
button_list_t mRadioButtons;
BOOL mHasBorder;
diff --git a/indra/llui/llresizebar.cpp b/indra/llui/llresizebar.cpp
index 304ac64f31..02f60c76fa 100644
--- a/indra/llui/llresizebar.cpp
+++ b/indra/llui/llresizebar.cpp
@@ -2,31 +2,25 @@
* @file llresizebar.cpp
* @brief LLResizeBar base class
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -143,6 +137,13 @@ BOOL LLResizeBar::handleHover(S32 x, S32 y, MASK mask)
if( valid_rect.localPointInRect( screen_x, screen_y ) && mResizingView )
{
+ // undock floater when user resize it
+ LLFloater* parent = dynamic_cast<LLFloater*>( getParent());
+ if (parent && parent->isDocked())
+ {
+ parent->setDocked( false, false);
+ }
+
// Resize the parent
LLRect orig_rect = mResizingView->getRect();
LLRect scaled_rect = orig_rect;
@@ -175,6 +176,11 @@ BOOL LLResizeBar::handleHover(S32 x, S32 y, MASK mask)
break;
}
+ notifyParent(LLSD().with("action", "resize")
+ .with("view_name", mResizingView->getName())
+ .with("new_height", new_height)
+ .with("new_width", new_width));
+
scaled_rect.mTop = scaled_rect.mBottom + new_height;
scaled_rect.mRight = scaled_rect.mLeft + new_width;
mResizingView->setRect(scaled_rect);
diff --git a/indra/llui/llresizebar.h b/indra/llui/llresizebar.h
index 4ad3d5035a..0725fbd846 100644
--- a/indra/llui/llresizebar.h
+++ b/indra/llui/llresizebar.h
@@ -2,31 +2,25 @@
* @file llresizebar.h
* @brief LLResizeBar base class
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -52,11 +46,11 @@ public:
Optional<bool> allow_double_click_snapping;
Params()
- : max_size("", S32_MAX),
- snapping_enabled("", true),
+ : max_size("max_size", S32_MAX),
+ snapping_enabled("snapping_enabled", true),
resizing_view("resizing_view"),
side("side"),
- allow_double_click_snapping("", true)
+ allow_double_click_snapping("allow_double_click_snapping", true)
{
name = "resize_bar";
}
diff --git a/indra/llui/llresizehandle.cpp b/indra/llui/llresizehandle.cpp
index 943e2f55f1..c3a51c36c9 100644
--- a/indra/llui/llresizehandle.cpp
+++ b/indra/llui/llresizehandle.cpp
@@ -2,31 +2,25 @@
* @file llresizehandle.cpp
* @brief LLResizeHandle base class
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -45,7 +39,9 @@
const S32 RESIZE_BORDER_WIDTH = 3;
LLResizeHandle::Params::Params()
-: corner("corner")
+: corner("corner"),
+ min_width("min_width"),
+ min_height("min_height")
{
name = "resize_handle";
}
@@ -63,7 +59,7 @@ LLResizeHandle::LLResizeHandle(const LLResizeHandle::Params& p)
{
if( RIGHT_BOTTOM == mCorner)
{
- mImage = LLUI::getUIImage("resize_handle_bottom_right_blue.tga");
+ mImage = LLUI::getUIImage("Resize_Corner");
}
switch( p.corner )
{
@@ -133,6 +129,13 @@ BOOL LLResizeHandle::handleHover(S32 x, S32 y, MASK mask)
LLView* resizing_view = getParent();
if( resizing_view )
{
+ // undock floater when user resize it
+ LLFloater* floater_parent = dynamic_cast<LLFloater*>(getParent());
+ if (floater_parent && floater_parent->isDocked())
+ {
+ floater_parent->setDocked(false, false);
+ }
+
// Resize the parent
LLRect orig_rect = resizing_view->getRect();
LLRect scaled_rect = orig_rect;
diff --git a/indra/llui/llresizehandle.h b/indra/llui/llresizehandle.h
index e4e3c81cec..531eb1db61 100644
--- a/indra/llui/llresizehandle.h
+++ b/indra/llui/llresizehandle.h
@@ -2,31 +2,25 @@
* @file llresizehandle.h
* @brief LLResizeHandle base class
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -35,7 +29,6 @@
#include "stdtypes.h"
#include "llview.h"
-#include "llimagegl.h"
#include "llcoord.h"
@@ -77,8 +70,8 @@ private:
const ECorner mCorner;
};
-const S32 RESIZE_HANDLE_HEIGHT = 16;
-const S32 RESIZE_HANDLE_WIDTH = 16;
+const S32 RESIZE_HANDLE_HEIGHT = 11;
+const S32 RESIZE_HANDLE_WIDTH = 11;
#endif // LL_RESIZEHANDLE_H
diff --git a/indra/llui/llresmgr.cpp b/indra/llui/llresmgr.cpp
index a4e23a605b..39385786bc 100644
--- a/indra/llui/llresmgr.cpp
+++ b/indra/llui/llresmgr.cpp
@@ -2,31 +2,25 @@
* @file llresmgr.cpp
* @brief Localized resource manager
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -279,6 +273,14 @@ std::string LLResMgr::getMonetaryString( S32 input ) const
void LLResMgr::getIntegerString( std::string& output, S32 input ) const
{
+ // handle special case of input value being zero
+ if (input == 0)
+ {
+ output = "0";
+ return;
+ }
+
+ // *NOTE: this method does not handle negative input integers correctly
S32 fraction = 0;
std::string fraction_string;
S32 remaining_count = input;
@@ -290,11 +292,11 @@ void LLResMgr::getIntegerString( std::string& output, S32 input ) const
{
if (fraction == remaining_count)
{
- fraction_string = llformat("%d%c", fraction, getThousandsSeparator());
+ fraction_string = llformat_to_utf8("%d%c", fraction, getThousandsSeparator());
}
else
{
- fraction_string = llformat("%3.3d%c", fraction, getThousandsSeparator());
+ fraction_string = llformat_to_utf8("%3.3d%c", fraction, getThousandsSeparator());
}
output = fraction_string + output;
}
diff --git a/indra/llui/llresmgr.h b/indra/llui/llresmgr.h
index c8fa340990..a652dcd2c0 100644
--- a/indra/llui/llresmgr.h
+++ b/indra/llui/llresmgr.h
@@ -2,31 +2,25 @@
* @file llresmgr.h
* @brief Localized resource manager
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llui/llrngwriter.cpp b/indra/llui/llrngwriter.cpp
new file mode 100644
index 0000000000..50b7bbab90
--- /dev/null
+++ b/indra/llui/llrngwriter.cpp
@@ -0,0 +1,310 @@
+/**
+ * @file llrngwriter.cpp
+ * @brief Generates Relax NG schema from param blocks
+ *
+ * $LicenseInfo:firstyear=2003&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llrngwriter.h"
+#include "lluicolor.h"
+#include "lluictrlfactory.h"
+
+//
+// LLRNGWriter - writes Relax NG schema files based on a param block
+//
+LLRNGWriter::LLRNGWriter()
+{
+ // register various callbacks for inspecting the contents of a param block
+ registerInspectFunc<bool>(boost::bind(&LLRNGWriter::writeAttribute, this, "boolean", _1, _2, _3, _4));
+ registerInspectFunc<std::string>(boost::bind(&LLRNGWriter::writeAttribute, this, "string", _1, _2, _3, _4));
+ registerInspectFunc<U8>(boost::bind(&LLRNGWriter::writeAttribute, this, "unsignedByte", _1, _2, _3, _4));
+ registerInspectFunc<S8>(boost::bind(&LLRNGWriter::writeAttribute, this, "signedByte", _1, _2, _3, _4));
+ registerInspectFunc<U16>(boost::bind(&LLRNGWriter::writeAttribute, this, "unsignedShort", _1, _2, _3, _4));
+ registerInspectFunc<S16>(boost::bind(&LLRNGWriter::writeAttribute, this, "signedShort", _1, _2, _3, _4));
+ registerInspectFunc<U32>(boost::bind(&LLRNGWriter::writeAttribute, this, "unsignedInt", _1, _2, _3, _4));
+ registerInspectFunc<S32>(boost::bind(&LLRNGWriter::writeAttribute, this, "integer", _1, _2, _3, _4));
+ registerInspectFunc<F32>(boost::bind(&LLRNGWriter::writeAttribute, this, "float", _1, _2, _3, _4));
+ registerInspectFunc<F64>(boost::bind(&LLRNGWriter::writeAttribute, this, "double", _1, _2, _3, _4));
+ registerInspectFunc<LLColor4>(boost::bind(&LLRNGWriter::writeAttribute, this, "string", _1, _2, _3, _4));
+ registerInspectFunc<LLUIColor>(boost::bind(&LLRNGWriter::writeAttribute, this, "string", _1, _2, _3, _4));
+ registerInspectFunc<LLUUID>(boost::bind(&LLRNGWriter::writeAttribute, this, "string", _1, _2, _3, _4));
+ registerInspectFunc<LLSD>(boost::bind(&LLRNGWriter::writeAttribute, this, "string", _1, _2, _3, _4));
+}
+
+void LLRNGWriter::writeRNG(const std::string& type_name, LLXMLNodePtr node, const LLInitParam::BaseBlock& block, const std::string& xml_namespace)
+{
+ mGrammarNode = node;
+ mGrammarNode->setName("grammar");
+ mGrammarNode->createChild("xmlns", true)->setStringValue("http://relaxng.org/ns/structure/1.0");
+ mGrammarNode->createChild("datatypeLibrary", true)->setStringValue("http://www.w3.org/2001/XMLSchema-datatypes");
+ mGrammarNode->createChild("ns", true)->setStringValue(xml_namespace);
+
+ node = mGrammarNode->createChild("start", false);
+ node = node->createChild("ref", false);
+ node->createChild("name", true)->setStringValue(type_name);
+
+ addDefinition(type_name, block);
+}
+
+void LLRNGWriter::addDefinition(const std::string& type_name, const LLInitParam::BaseBlock& block)
+{
+ if (mDefinedElements.find(type_name) != mDefinedElements.end()) return;
+ mDefinedElements.insert(type_name);
+
+ LLXMLNodePtr node = mGrammarNode->createChild("define", false);
+ node->createChild("name", true)->setStringValue(type_name);
+
+ mElementNode = node->createChild("element", false);
+ mElementNode->createChild("name", true)->setStringValue(type_name);
+ mChildrenNode = mElementNode->createChild("zeroOrMore", false)->createChild("choice", false);
+
+ mAttributesWritten.first = mElementNode;
+ mAttributesWritten.second.clear();
+ mElementsWritten.clear();
+
+ block.inspectBlock(*this);
+
+ // add includes for all possible children
+ const std::type_info* type = *LLWidgetTypeRegistry::instance().getValue(type_name);
+ const widget_registry_t* widget_registryp = LLChildRegistryRegistry::instance().getValue(type);
+
+ // add include declarations for all valid children
+ for (widget_registry_t::Registrar::registry_map_t::const_iterator it = widget_registryp->currentRegistrar().beginItems();
+ it != widget_registryp->currentRegistrar().endItems();
+ ++it)
+ {
+ std::string child_name = it->first;
+ if (child_name == type_name)
+ {
+ continue;
+ }
+
+ LLXMLNodePtr old_element_node = mElementNode;
+ LLXMLNodePtr old_child_node = mChildrenNode;
+ //FIXME: add LLDefaultParamBlockRegistry back when working on schema generation
+ //addDefinition(child_name, (*LLDefaultParamBlockRegistry::instance().getValue(type))());
+ mElementNode = old_element_node;
+ mChildrenNode = old_child_node;
+
+ mChildrenNode->createChild("ref", false)->createChild("name", true)->setStringValue(child_name);
+ }
+
+ if (mChildrenNode->mChildren.isNull())
+ {
+ // remove unused children node
+ mChildrenNode->mParent->mParent->deleteChild(mChildrenNode->mParent);
+ }
+}
+
+void LLRNGWriter::writeAttribute(const std::string& type, const Parser::name_stack_t& stack, S32 min_count, S32 max_count, const std::vector<std::string>* possible_values)
+{
+ if (max_count == 0) return;
+
+ name_stack_t non_empty_names;
+ std::string attribute_name;
+ for (name_stack_t::const_iterator it = stack.begin();
+ it != stack.end();
+ ++it)
+ {
+ const std::string& name = it->first;
+ if (!name.empty())
+ {
+ non_empty_names.push_back(*it);
+ }
+ }
+
+ if (non_empty_names.empty()) return;
+
+ for (name_stack_t::const_iterator it = non_empty_names.begin();
+ it != non_empty_names.end();
+ ++it)
+ {
+ if (!attribute_name.empty())
+ {
+ attribute_name += ".";
+ }
+ attribute_name += it->first;
+ }
+
+ // singular attribute, e.g. <foo bar="1"/>
+ if (non_empty_names.size() == 1 && max_count == 1)
+ {
+ if (mAttributesWritten.second.find(attribute_name) == mAttributesWritten.second.end())
+ {
+ LLXMLNodePtr node = createCardinalityNode(mElementNode, min_count, max_count)->createChild("attribute", false);
+ node->createChild("name", true)->setStringValue(attribute_name);
+ node->createChild("data", false)->createChild("type", true)->setStringValue(type);
+
+ mAttributesWritten.second.insert(attribute_name);
+ }
+ }
+ // compound attribute
+ else
+ {
+ std::string element_name;
+
+ // traverse all but last element, leaving that as an attribute name
+ name_stack_t::const_iterator end_it = non_empty_names.end();
+ end_it--;
+
+ for (name_stack_t::const_iterator it = non_empty_names.begin();
+ it != end_it;
+ ++it)
+ {
+ if (it != non_empty_names.begin())
+ {
+ element_name += ".";
+ }
+ element_name += it->first;
+ }
+
+ elements_map_t::iterator found_it = mElementsWritten.find(element_name);
+ // <choice>
+ // <group>
+ // <optional>
+ // <attribute name="foo.bar"><data type="string"/></attribute>
+ // </optional>
+ // <optional>
+ // <attribute name="foo.baz"><data type="integer"/></attribute>
+ // </optional>
+ // </group>
+ // <element name="foo">
+ // <optional>
+ // <attribute name="bar"><data type="string"/></attribute>
+ // </optional>
+ // <optional>
+ // <attribute name="baz"><data type="string"/></attribute>
+ // </optional>
+ // </element>
+ // <element name="outer.foo">
+ // <ref name="foo"/>
+ // </element>
+ // </choice>
+
+ if (found_it != mElementsWritten.end())
+ {
+ // reuse existing element
+ LLXMLNodePtr choice_node = found_it->second.first;
+
+ // attribute with this name not already written?
+ if (found_it->second.second.find(attribute_name) == found_it->second.second.end())
+ {
+ // append to <group>
+ LLXMLNodePtr node = choice_node->mChildren->head;
+ node = createCardinalityNode(node, min_count, max_count)->createChild("attribute", false);
+ node->createChild("name", true)->setStringValue(attribute_name);
+ addTypeNode(node, type, possible_values);
+
+ // append to <element>
+ node = choice_node->mChildren->head->mNext->mChildren->head;
+ node = createCardinalityNode(node, min_count, max_count)->createChild("attribute", false);
+ node->createChild("name", true)->setStringValue(non_empty_names.back().first);
+ addTypeNode(node, type, possible_values);
+
+ // append to <element>
+ //node = choice_node->mChildren->head->mNext->mNext->mChildren->head;
+ //node = createCardinalityNode(node, min_count, max_count)->createChild("attribute", false);
+ //node->createChild("name", true)->setStringValue(non_empty_names.back().first);
+ //addTypeNode(node, type, possible_values);
+
+ found_it->second.second.insert(attribute_name);
+ }
+ }
+ else
+ {
+ LLXMLNodePtr choice_node = mElementNode->createChild("choice", false);
+
+ LLXMLNodePtr node = choice_node->createChild("group", false);
+ node = createCardinalityNode(node, min_count, max_count)->createChild("attribute", false);
+ node->createChild("name", true)->setStringValue(attribute_name);
+ addTypeNode(node, type, possible_values);
+
+ node = choice_node->createChild("optional", false);
+ node = node->createChild("element", false);
+ node->createChild("name", true)->setStringValue(element_name);
+ node = createCardinalityNode(node, min_count, max_count)->createChild("attribute", false);
+ node->createChild("name", true)->setStringValue(non_empty_names.back().first);
+ addTypeNode(node, type, possible_values);
+
+ //node = choice_node->createChild("optional", false);
+ //node = node->createChild("element", false);
+ //node->createChild("name", true)->setStringValue(mDefinitionName + "." + element_name);
+ //node = createCardinalityNode(node, min_count, max_count)->createChild("attribute", false);
+ //node->createChild("name", true)->setStringValue(non_empty_names.back().first);
+ //addTypeNode(node, type, possible_values);
+
+ attribute_data_t& attribute_data = mElementsWritten[element_name];
+ attribute_data.first = choice_node;
+ attribute_data.second.insert(attribute_name);
+ }
+ }
+}
+
+void LLRNGWriter::addTypeNode(LLXMLNodePtr parent_node, const std::string& type, const std::vector<std::string>* possible_values)
+{
+ if (possible_values)
+ {
+ LLXMLNodePtr enum_node = parent_node->createChild("choice", false);
+ for (std::vector<std::string>::const_iterator it = possible_values->begin();
+ it != possible_values->end();
+ ++it)
+ {
+ enum_node->createChild("value", false)->setStringValue(*it);
+ }
+ }
+ else
+ {
+ parent_node->createChild("data", false)->createChild("type", true)->setStringValue(type);
+ }
+}
+
+LLXMLNodePtr LLRNGWriter::createCardinalityNode(LLXMLNodePtr parent_node, S32 min_count, S32 max_count)
+{
+ // unlinked by default, meaning this attribute is forbidden
+ LLXMLNodePtr count_node = new LLXMLNode();
+ if (min_count == 0)
+ {
+ if (max_count == 1)
+ {
+ count_node = parent_node->createChild("optional", false);
+ }
+ else if (max_count > 1)
+ {
+ count_node = parent_node->createChild("zeroOrMore", false);
+ }
+ }
+ else if (min_count >= 1)
+ {
+ if (max_count == 1 && min_count == 1)
+ {
+ // just add raw element, will count as 1 and only 1
+ count_node = parent_node;
+ }
+ else
+ {
+ count_node = parent_node->createChild("oneOrMore", false);
+ }
+ }
+ return count_node;
+}
diff --git a/indra/llui/llrngwriter.h b/indra/llui/llrngwriter.h
new file mode 100644
index 0000000000..c33aa28613
--- /dev/null
+++ b/indra/llui/llrngwriter.h
@@ -0,0 +1,63 @@
+/**
+ * @file llrngwriter.h
+ * @brief Generates Relax NG schema files from a param block
+ *
+ * $LicenseInfo:firstyear=2003&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LLRNGWRITER_H
+#define LLRNGWRITER_H
+
+#include "llinitparam.h"
+#include "llxmlnode.h"
+
+class LLRNGWriter : public LLInitParam::Parser
+{
+ LOG_CLASS(LLRNGWriter);
+public:
+ void writeRNG(const std::string& name, LLXMLNodePtr node, const LLInitParam::BaseBlock& block, const std::string& xml_namespace);
+ void addDefinition(const std::string& type_name, const LLInitParam::BaseBlock& block);
+
+ /*virtual*/ std::string getCurrentElementName() { return LLStringUtil::null; }
+
+ LLRNGWriter();
+
+private:
+ LLXMLNodePtr createCardinalityNode(LLXMLNodePtr parent_node, S32 min_count, S32 max_count);
+ void addTypeNode(LLXMLNodePtr parent_node, const std::string& type, const std::vector<std::string>* possible_values);
+
+ void writeAttribute(const std::string& type, const Parser::name_stack_t&, S32 min_count, S32 max_count, const std::vector<std::string>* possible_values);
+ LLXMLNodePtr mElementNode;
+ LLXMLNodePtr mChildrenNode;
+ LLXMLNodePtr mGrammarNode;
+ std::string mDefinitionName;
+
+ typedef std::pair<LLXMLNodePtr, std::set<std::string> > attribute_data_t;
+ typedef std::map<std::string, attribute_data_t> elements_map_t;
+ typedef std::set<std::string> defined_elements_t;
+
+ defined_elements_t mDefinedElements;
+ attribute_data_t mAttributesWritten;
+ elements_map_t mElementsWritten;
+};
+
+#endif //LLRNGWRITER_H
diff --git a/indra/llui/llscrollbar.cpp b/indra/llui/llscrollbar.cpp
index 3f1ff34419..3a867a10a7 100644
--- a/indra/llui/llscrollbar.cpp
+++ b/indra/llui/llscrollbar.cpp
@@ -2,31 +2,25 @@
* @file llscrollbar.cpp
* @brief Scrollbar UI widget
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -48,7 +42,7 @@
#include "llrender.h"
#include "lluictrlfactory.h"
-static LLDefaultWidgetRegistry::Register<LLScrollbar> register_scrollbar("scroll_bar");
+static LLDefaultChildRegistry::Register<LLScrollbar> register_scrollbar("scroll_bar");
LLScrollbar::Params::Params()
: orientation ("orientation", HORIZONTAL),
@@ -56,15 +50,19 @@ LLScrollbar::Params::Params()
doc_pos ("doc_pos", 0),
page_size ("page_size", 0),
step_size ("step_size", 1),
- thumb_image("thumb_image"),
- track_image("track_image"),
+ thumb_image_vertical("thumb_image_vertical"),
+ thumb_image_horizontal("thumb_image_horizontal"),
+ track_image_vertical("track_image_vertical"),
+ track_image_horizontal("track_image_horizontal"),
track_color("track_color"),
thumb_color("thumb_color"),
thickness("thickness"),
up_button("up_button"),
down_button("down_button"),
left_button("left_button"),
- right_button("right_button")
+ right_button("right_button"),
+ bg_visible("bg_visible", false),
+ bg_color("bg_color", LLColor4::black)
{
tab_stop = false;
}
@@ -84,11 +82,13 @@ LLScrollbar::LLScrollbar(const Params & p)
mCurGlowStrength(0.f),
mTrackColor( p.track_color() ),
mThumbColor ( p.thumb_color() ),
- mOnScrollEndCallback( NULL ),
- mOnScrollEndData( NULL ),
- mThumbImage(p.thumb_image),
- mTrackImage(p.track_image),
- mThickness(p.thickness.isProvided() ? p.thickness : LLUI::sSettingGroups["config"]->getS32("UIScrollbarSize"))
+ mThumbImageV(p.thumb_image_vertical),
+ mThumbImageH(p.thumb_image_horizontal),
+ mTrackImageV(p.track_image_vertical),
+ mTrackImageH(p.track_image_horizontal),
+ mThickness(p.thickness.isProvided() ? p.thickness : LLUI::sSettingGroups["config"]->getS32("UIScrollbarSize")),
+ mBGVisible(p.bg_visible),
+ mBGColor(p.bg_color)
{
updateThumbRect();
@@ -143,7 +143,8 @@ void LLScrollbar::setDocParams( S32 size, S32 pos )
updateThumbRect();
}
-void LLScrollbar::setDocPos(S32 pos, BOOL update_thumb)
+// returns true if document position really changed
+bool LLScrollbar::setDocPos(S32 pos, BOOL update_thumb)
{
pos = llclamp(pos, 0, getDocPosMax());
if (pos != mDocPos)
@@ -160,7 +161,9 @@ void LLScrollbar::setDocPos(S32 pos, BOOL update_thumb)
{
updateThumbRect();
}
+ return true;
}
+ return false;
}
void LLScrollbar::setDocSize(S32 size)
@@ -235,11 +238,6 @@ void LLScrollbar::updateThumbRect()
mThumbRect.mRight = thumb_start + thumb_length;
mThumbRect.mBottom = 0;
}
-
- if (mOnScrollEndCallback && mOnScrollEndData && (mDocPos == getDocPosMax()))
- {
- mOnScrollEndCallback(mOnScrollEndData);
- }
}
BOOL LLScrollbar::handleMouseDown(S32 x, S32 y, MASK mask)
@@ -390,7 +388,7 @@ BOOL LLScrollbar::handleHover(S32 x, S32 y, MASK mask)
}
else
{
- handled = childrenHandleMouseUp( x, y, mask ) != NULL;
+ handled = childrenHandleHover( x, y, mask ) != NULL;
}
// Opaque
@@ -408,8 +406,8 @@ BOOL LLScrollbar::handleHover(S32 x, S32 y, MASK mask)
BOOL LLScrollbar::handleScrollWheel(S32 x, S32 y, S32 clicks)
{
- changeLine( clicks * mStepSize, TRUE );
- return TRUE;
+ BOOL handled = changeLine( clicks * mStepSize, TRUE );
+ return handled;
}
BOOL LLScrollbar::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
@@ -451,6 +449,13 @@ BOOL LLScrollbar::handleMouseUp(S32 x, S32 y, MASK mask)
return handled;
}
+BOOL LLScrollbar::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ // just treat a double click as a second click
+ return handleMouseDown(x, y, mask);
+}
+
+
void LLScrollbar::reshape(S32 width, S32 height, BOOL called_from_parent)
{
if (width == getRect().getWidth() && height == getRect().getHeight()) return;
@@ -478,9 +483,14 @@ void LLScrollbar::draw()
{
if (!getRect().isValid()) return;
+ if(mBGVisible)
+ {
+ gl_rect_2d(getLocalRect(), mBGColor.get(), TRUE);
+ }
+
S32 local_mouse_x;
S32 local_mouse_y;
- LLUI::getCursorPositionLocal(this, &local_mouse_x, &local_mouse_y);
+ LLUI::getMousePositionLocal(this, &local_mouse_x, &local_mouse_y);
BOOL other_captor = gFocusMgr.getMouseCapture() && gFocusMgr.getMouseCapture() != this;
BOOL hovered = getEnabled() && !other_captor && (hasMouseCapture() || mThumbRect.pointInRect(local_mouse_x, local_mouse_y));
if (hovered)
@@ -493,7 +503,8 @@ void LLScrollbar::draw()
}
// Draw background and thumb.
- if (mTrackImage.isNull() || mThumbImage.isNull())
+ if ( ( mOrientation == VERTICAL&&(mThumbImageV.isNull() || mThumbImageH.isNull()) )
+ || (mOrientation == HORIZONTAL&&(mTrackImageH.isNull() || mTrackImageV.isNull()) ))
{
gl_rect_2d(mOrientation == HORIZONTAL ? mThickness : 0,
mOrientation == VERTICAL ? getRect().getHeight() - 2 * mThickness : getRect().getHeight(),
@@ -505,36 +516,53 @@ void LLScrollbar::draw()
}
else
{
- // Background
- mTrackImage->drawSolid(mOrientation == HORIZONTAL ? mThickness : 0,
- mOrientation == VERTICAL ? mThickness : 0,
- mOrientation == HORIZONTAL ? getRect().getWidth() - 2 * mThickness : getRect().getWidth(),
- mOrientation == VERTICAL ? getRect().getHeight() - 2 * mThickness : getRect().getHeight(),
- mTrackColor.get());
-
// Thumb
LLRect outline_rect = mThumbRect;
outline_rect.stretch(2);
-
- if (gFocusMgr.getKeyboardFocus() == this)
+ // Background
+
+ if(mOrientation == HORIZONTAL)
{
- mTrackImage->draw(outline_rect, gFocusMgr.getFocusColor());
+ mTrackImageH->drawSolid(mThickness //S32 x
+ , 0 //S32 y
+ , getRect().getWidth() - 2 * mThickness //S32 width
+ , getRect().getHeight() //S32 height
+ , mTrackColor.get()); //const LLColor4& color
+
+ if (gFocusMgr.getKeyboardFocus() == this)
+ {
+ mTrackImageH->draw(outline_rect, gFocusMgr.getFocusColor());
+ }
+
+ mThumbImageH->draw(mThumbRect, mThumbColor.get());
+ if (mCurGlowStrength > 0.01f)
+ {
+ gGL.setSceneBlendType(LLRender::BT_ADD_WITH_ALPHA);
+ mThumbImageH->drawSolid(mThumbRect, LLColor4(1.f, 1.f, 1.f, mCurGlowStrength));
+ gGL.setSceneBlendType(LLRender::BT_ALPHA);
+ }
+
}
-
- mThumbImage->draw(mThumbRect, mThumbColor.get());
- if (mCurGlowStrength > 0.01f)
+ else if(mOrientation == VERTICAL)
{
- gGL.setSceneBlendType(LLRender::BT_ADD_WITH_ALPHA);
- mThumbImage->drawSolid(mThumbRect, LLColor4(1.f, 1.f, 1.f, mCurGlowStrength));
- gGL.setSceneBlendType(LLRender::BT_ALPHA);
+ mTrackImageV->drawSolid( 0 //S32 x
+ , mThickness //S32 y
+ , getRect().getWidth() //S32 width
+ , getRect().getHeight() - 2 * mThickness //S32 height
+ , mTrackColor.get()); //const LLColor4& color
+ if (gFocusMgr.getKeyboardFocus() == this)
+ {
+ mTrackImageV->draw(outline_rect, gFocusMgr.getFocusColor());
+ }
+
+ mThumbImageV->draw(mThumbRect, mThumbColor.get());
+ if (mCurGlowStrength > 0.01f)
+ {
+ gGL.setSceneBlendType(LLRender::BT_ADD_WITH_ALPHA);
+ mThumbImageV->drawSolid(mThumbRect, LLColor4(1.f, 1.f, 1.f, mCurGlowStrength));
+ gGL.setSceneBlendType(LLRender::BT_ALPHA);
+ }
}
-
- }
-
- BOOL was_scrolled_to_bottom = (getDocPos() == getDocPosMax());
- if (mOnScrollEndCallback && was_scrolled_to_bottom)
- {
- mOnScrollEndCallback(mOnScrollEndData);
}
// Draw children
@@ -542,9 +570,9 @@ void LLScrollbar::draw()
} // end draw
-void LLScrollbar::changeLine( S32 delta, BOOL update_thumb )
+bool LLScrollbar::changeLine( S32 delta, BOOL update_thumb )
{
- setDocPos(mDocPos + delta, update_thumb);
+ return setDocPos(mDocPos + delta, update_thumb);
}
void LLScrollbar::setValue(const LLSD& value)
@@ -616,15 +644,3 @@ void LLScrollbar::onLineDownBtnPressed( const LLSD& data )
{
changeLine( mStepSize, TRUE );
}
-
-
-namespace LLInitParam
-{
- template<>
- bool ParamCompare<boost::function<void (S32, LLScrollbar*)> >::equals(
- const boost::function<void (S32, LLScrollbar*)> &a,
- const boost::function<void (S32, LLScrollbar*)> &b)
- {
- return false;
- }
-}
diff --git a/indra/llui/llscrollbar.h b/indra/llui/llscrollbar.h
index 43604d37b7..ff74f753b9 100644
--- a/indra/llui/llscrollbar.h
+++ b/indra/llui/llscrollbar.h
@@ -2,31 +2,25 @@
* @file llscrollbar.h
* @brief Scrollbar UI widget
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -61,11 +55,16 @@ public:
Optional<S32> step_size;
Optional<S32> thickness;
- Optional<LLUIImage*> thumb_image,
- track_image;
+ Optional<LLUIImage*> thumb_image_vertical,
+ thumb_image_horizontal,
+ track_image_horizontal,
+ track_image_vertical;
+
+ Optional<bool> bg_visible;
Optional<LLUIColor> track_color,
- thumb_color;
+ thumb_color,
+ bg_color;
Optional<LLButton::Params> up_button;
Optional<LLButton::Params> down_button;
@@ -88,6 +87,7 @@ public:
virtual BOOL handleKeyHere(KEY key, MASK mask);
virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask);
virtual BOOL handleHover(S32 x, S32 y, MASK mask);
virtual BOOL handleScrollWheel(S32 x, S32 y, S32 clicks);
virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
@@ -103,7 +103,7 @@ public:
// How many "lines" the "document" has scrolled.
// 0 <= DocPos <= DocSize - DocVisibile
- void setDocPos( S32 pos, BOOL update_thumb = TRUE );
+ bool setDocPos( S32 pos, BOOL update_thumb = TRUE );
S32 getDocPos() const { return mDocPos; }
BOOL isAtBeginning();
@@ -125,14 +125,9 @@ public:
void onLineUpBtnPressed(const LLSD& data);
void onLineDownBtnPressed(const LLSD& data);
- void setTrackColor( const LLColor4& color ) { mTrackColor = color; }
- void setThumbColor( const LLColor4& color ) { mThumbColor = color; }
-
- void setOnScrollEndCallback(void (*callback)(void*), void* userdata) { mOnScrollEndCallback = callback; mOnScrollEndData = userdata;}
-
private:
void updateThumbRect();
- void changeLine(S32 delta, BOOL update_thumb );
+ bool changeLine(S32 delta, BOOL update_thumb );
callback_t mChangeCallback;
@@ -154,22 +149,17 @@ private:
LLUIColor mTrackColor;
LLUIColor mThumbColor;
+ LLUIColor mBGColor;
- LLUIImagePtr mThumbImage;
- LLUIImagePtr mTrackImage;
+ bool mBGVisible;
- S32 mThickness;
+ LLUIImagePtr mThumbImageV;
+ LLUIImagePtr mThumbImageH;
+ LLUIImagePtr mTrackImageV;
+ LLUIImagePtr mTrackImageH;
- void (*mOnScrollEndCallback)(void*);
- void *mOnScrollEndData;
+ S32 mThickness;
};
-namespace LLInitParam
-{
- template<>
- bool ParamCompare<boost::function<void (S32, LLScrollbar*)> >::equals(
- const boost::function<void (S32, LLScrollbar*)> &a, const boost::function<void (S32, LLScrollbar*)> &b);
-}
-
#endif // LL_SCROLLBAR_H
diff --git a/indra/llui/llscrollcontainer.cpp b/indra/llui/llscrollcontainer.cpp
index 2a2e56a92c..3146418a7d 100644
--- a/indra/llui/llscrollcontainer.cpp
+++ b/indra/llui/llscrollcontainer.cpp
@@ -2,31 +2,25 @@
* @file llscrollcontainer.cpp
* @brief LLScrollContainer base class
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -37,6 +31,7 @@
#include "llrender.h"
#include "llcontainerview.h"
+#include "lllocalcliprect.h"
// #include "llfolderview.h"
#include "llscrollingpanellist.h"
#include "llscrollbar.h"
@@ -55,19 +50,29 @@
static const S32 HORIZONTAL_MULTIPLE = 8;
static const S32 VERTICAL_MULTIPLE = 16;
-static const F32 MIN_AUTO_SCROLL_RATE = 120.f;
-static const F32 MAX_AUTO_SCROLL_RATE = 500.f;
static const F32 AUTO_SCROLL_RATE_ACCEL = 120.f;
///----------------------------------------------------------------------------
/// Class LLScrollContainer
///----------------------------------------------------------------------------
-static LLDefaultWidgetRegistry::Register<LLScrollContainer> r("scroll_container");
+static LLDefaultChildRegistry::Register<LLScrollContainer> r("scroll_container");
+
+#include "llscrollingpanellist.h"
+#include "llcontainerview.h"
+#include "llpanel.h"
+
+static ScrollContainerRegistry::Register<LLScrollingPanelList> r1("scrolling_panel_list");
+static ScrollContainerRegistry::Register<LLContainerView> r2("container_view");
+static ScrollContainerRegistry::Register<LLPanel> r3("panel", &LLPanel::fromXML);
LLScrollContainer::Params::Params()
: is_opaque("opaque"),
bg_color("color"),
+ border_visible("border_visible"),
+ hide_scrollbar("hide_scrollbar"),
+ min_auto_scroll_rate("min_auto_scroll_rate", 100),
+ max_auto_scroll_rate("max_auto_scroll_rate", 1000),
reserve_scroll_corner("reserve_scroll_corner", false)
{
name = "scroll_container";
@@ -83,7 +88,10 @@ LLScrollContainer::LLScrollContainer(const LLScrollContainer::Params& p)
mAutoScrollRate( 0.f ),
mBackgroundColor(p.bg_color()),
mIsOpaque(p.is_opaque),
+ mHideScrollbar(p.hide_scrollbar),
mReserveScrollCorner(p.reserve_scroll_corner),
+ mMinAutoScrollRate(p.min_auto_scroll_rate),
+ mMaxAutoScrollRate(p.max_auto_scroll_rate),
mScrolledView(NULL)
{
static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
@@ -91,12 +99,13 @@ LLScrollContainer::LLScrollContainer(const LLScrollContainer::Params& p)
LLViewBorder::Params params;
params.name("scroll border");
params.rect(border_rect);
- params.bevel_type(LLViewBorder::BEVEL_IN);
+ params.visible(p.border_visible);
+ params.bevel_style(LLViewBorder::BEVEL_IN);
mBorder = LLUICtrlFactory::create<LLViewBorder> (params);
LLView::addChild( mBorder );
mInnerRect.set( 0, getRect().getHeight(), getRect().getWidth(), 0 );
- mInnerRect.stretch( -mBorder->getBorderWidth() );
+ mInnerRect.stretch( -getBorderWidth() );
LLRect vertical_scroll_rect = mInnerRect;
vertical_scroll_rect.mLeft = vertical_scroll_rect.mRight - scrollbar_size;
@@ -108,12 +117,11 @@ LLScrollContainer::LLScrollContainer(const LLScrollContainer::Params& p)
sbparams.doc_pos(0);
sbparams.page_size(mInnerRect.getHeight());
sbparams.step_size(VERTICAL_MULTIPLE);
+ sbparams.follows.flags(FOLLOWS_RIGHT | FOLLOWS_TOP | FOLLOWS_BOTTOM);
+ sbparams.visible(false);
+ sbparams.change_callback(p.scroll_callback);
mScrollbar[VERTICAL] = LLUICtrlFactory::create<LLScrollbar> (sbparams);
LLView::addChild( mScrollbar[VERTICAL] );
- mScrollbar[VERTICAL]->setVisible( FALSE );
- mScrollbar[VERTICAL]->setFollowsRight();
- mScrollbar[VERTICAL]->setFollowsTop();
- mScrollbar[VERTICAL]->setFollowsBottom();
LLRect horizontal_scroll_rect = mInnerRect;
horizontal_scroll_rect.mTop = horizontal_scroll_rect.mBottom + scrollbar_size;
@@ -124,11 +132,11 @@ LLScrollContainer::LLScrollContainer(const LLScrollContainer::Params& p)
sbparams.doc_pos(0);
sbparams.page_size(mInnerRect.getWidth());
sbparams.step_size(VERTICAL_MULTIPLE);
+ sbparams.visible(false);
+ sbparams.follows.flags(FOLLOWS_LEFT | FOLLOWS_RIGHT);
+ sbparams.change_callback(p.scroll_callback);
mScrollbar[HORIZONTAL] = LLUICtrlFactory::create<LLScrollbar> (sbparams);
LLView::addChild( mScrollbar[HORIZONTAL] );
- mScrollbar[HORIZONTAL]->setVisible( FALSE );
- mScrollbar[HORIZONTAL]->setFollowsLeft();
- mScrollbar[HORIZONTAL]->setFollowsRight();
}
// Destroys the object
@@ -174,8 +182,8 @@ void LLScrollContainer::reshape(S32 width, S32 height,
{
LLUICtrl::reshape( width, height, called_from_parent );
- mInnerRect.set( 0, getRect().getHeight(), getRect().getWidth(), 0 );
- mInnerRect.stretch( -mBorder->getBorderWidth() );
+ mInnerRect = getLocalRect();
+ mInnerRect.stretch( -getBorderWidth() );
if (mScrolledView)
{
@@ -185,13 +193,14 @@ void LLScrollContainer::reshape(S32 width, S32 height,
S32 visible_height = 0;
BOOL show_v_scrollbar = FALSE;
BOOL show_h_scrollbar = FALSE;
- calcVisibleSize( scrolled_rect, &visible_width, &visible_height, &show_h_scrollbar, &show_v_scrollbar );
+ calcVisibleSize( &visible_width, &visible_height, &show_h_scrollbar, &show_v_scrollbar );
mScrollbar[VERTICAL]->setDocSize( scrolled_rect.getHeight() );
mScrollbar[VERTICAL]->setPageSize( visible_height );
mScrollbar[HORIZONTAL]->setDocSize( scrolled_rect.getWidth() );
mScrollbar[HORIZONTAL]->setPageSize( visible_width );
+ updateScroll();
}
}
@@ -202,7 +211,7 @@ BOOL LLScrollContainer::handleKeyHere(KEY key, MASK mask)
// NOTE: this should not recurse indefinitely as handleKeyHere
// should not propagate to parent controls, so mScrolledView should *not*
// call LLScrollContainer::handleKeyHere in turn
- if (mScrolledView->handleKeyHere(key, mask))
+ if (mScrolledView && mScrolledView->handleKeyHere(key, mask))
{
return TRUE;
}
@@ -210,6 +219,7 @@ BOOL LLScrollContainer::handleKeyHere(KEY key, MASK mask)
{
if( mScrollbar[i]->handleKeyHere(key, mask) )
{
+ updateScroll();
return TRUE;
}
}
@@ -219,39 +229,37 @@ BOOL LLScrollContainer::handleKeyHere(KEY key, MASK mask)
BOOL LLScrollContainer::handleScrollWheel( S32 x, S32 y, S32 clicks )
{
- for( S32 i = 0; i < SCROLLBAR_COUNT; i++ )
- {
- // Note: tries vertical and then horizontal
+ // Give event to my child views - they may have scroll bars
+ // (Bad UI design, but technically possible.)
+ if (LLUICtrl::handleScrollWheel(x,y,clicks))
+ return TRUE;
+ // When the vertical scrollbar is visible, scroll wheel
+ // only affects vertical scrolling. It's confusing to have
+ // scroll wheel perform both vertical and horizontal in a
+ // single container.
+ LLScrollbar* vertical = mScrollbar[VERTICAL];
+ if (vertical->getVisible()
+ && vertical->getEnabled())
+ {
// Pretend the mouse is over the scrollbar
- if( mScrollbar[i]->handleScrollWheel( 0, 0, clicks ) )
+ if (vertical->handleScrollWheel( 0, 0, clicks ) )
{
- return TRUE;
+ updateScroll();
}
+ // Always eat the event
+ return TRUE;
}
- // Eat scroll wheel event (to avoid scrolling nested containers?)
- return TRUE;
-}
-
-BOOL LLScrollContainer::needsToScroll(S32 x, S32 y, LLScrollContainer::SCROLL_ORIENTATION axis) const
-{
- if(mScrollbar[axis]->getVisible())
+ LLScrollbar* horizontal = mScrollbar[HORIZONTAL];
+ // Test enablement and visibility for consistency with
+ // LLView::childrenHandleScrollWheel().
+ if (horizontal->getVisible()
+ && horizontal->getEnabled()
+ && horizontal->handleScrollWheel( 0, 0, clicks ) )
{
- LLRect inner_rect_local( 0, mInnerRect.getHeight(), mInnerRect.getWidth(), 0 );
- const S32 AUTOSCROLL_SIZE = 10;
- if(mScrollbar[axis]->getVisible())
- {
- static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
- inner_rect_local.mRight -= scrollbar_size;
- inner_rect_local.mTop += AUTOSCROLL_SIZE;
- inner_rect_local.mBottom = inner_rect_local.mTop - AUTOSCROLL_SIZE;
- }
- if( inner_rect_local.pointInRect( x, y ) && (mScrollbar[axis]->getDocPos() > 0) )
- {
- return TRUE;
- }
-
+ updateScroll();
+ return TRUE;
}
return FALSE;
}
@@ -266,12 +274,27 @@ BOOL LLScrollContainer::handleDragAndDrop(S32 x, S32 y, MASK mask,
static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
// Scroll folder view if needed. Never accepts a drag or drop.
*accept = ACCEPT_NO;
- BOOL handled = FALSE;
+ BOOL handled = autoScroll(x, y);
+
+ if( !handled )
+ {
+ handled = childrenHandleDragAndDrop(x, y, mask, drop, cargo_type,
+ cargo_data, accept, tooltip_msg) != NULL;
+ }
+
+ return TRUE;
+}
+
+bool LLScrollContainer::autoScroll(S32 x, S32 y)
+{
+ static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
+
+ bool scrolling = false;
if( mScrollbar[HORIZONTAL]->getVisible() || mScrollbar[VERTICAL]->getVisible() )
{
- const S32 AUTOSCROLL_SIZE = 10;
- S32 auto_scroll_speed = llround(mAutoScrollRate * LLFrameTimer::getFrameDeltaTimeF32());
-
+ LLRect screen_local_extents;
+ screenRectToLocal(getRootView()->getLocalRect(), &screen_local_extents);
+
LLRect inner_rect_local( 0, mInnerRect.getHeight(), mInnerRect.getWidth(), 0 );
if( mScrollbar[HORIZONTAL]->getVisible() )
{
@@ -282,119 +305,92 @@ BOOL LLScrollContainer::handleDragAndDrop(S32 x, S32 y, MASK mask,
inner_rect_local.mRight -= scrollbar_size;
}
+ // clip rect against root view
+ inner_rect_local.intersectWith(screen_local_extents);
+
+ S32 auto_scroll_speed = llround(mAutoScrollRate * LLFrameTimer::getFrameDeltaTimeF32());
+ // autoscroll region should take up no more than one third of visible scroller area
+ S32 auto_scroll_region_width = llmin(inner_rect_local.getWidth() / 3, 10);
+ S32 auto_scroll_region_height = llmin(inner_rect_local.getHeight() / 3, 10);
+
if( mScrollbar[HORIZONTAL]->getVisible() )
{
- LLRect left_scroll_rect = inner_rect_local;
- left_scroll_rect.mRight = AUTOSCROLL_SIZE;
+ LLRect left_scroll_rect = screen_local_extents;
+ left_scroll_rect.mRight = inner_rect_local.mLeft + auto_scroll_region_width;
if( left_scroll_rect.pointInRect( x, y ) && (mScrollbar[HORIZONTAL]->getDocPos() > 0) )
{
mScrollbar[HORIZONTAL]->setDocPos( mScrollbar[HORIZONTAL]->getDocPos() - auto_scroll_speed );
mAutoScrolling = TRUE;
- handled = TRUE;
+ scrolling = true;
}
- LLRect right_scroll_rect = inner_rect_local;
- right_scroll_rect.mLeft = inner_rect_local.mRight - AUTOSCROLL_SIZE;
+ LLRect right_scroll_rect = screen_local_extents;
+ right_scroll_rect.mLeft = inner_rect_local.mRight - auto_scroll_region_width;
if( right_scroll_rect.pointInRect( x, y ) && (mScrollbar[HORIZONTAL]->getDocPos() < mScrollbar[HORIZONTAL]->getDocPosMax()) )
{
mScrollbar[HORIZONTAL]->setDocPos( mScrollbar[HORIZONTAL]->getDocPos() + auto_scroll_speed );
mAutoScrolling = TRUE;
- handled = TRUE;
+ scrolling = true;
}
}
if( mScrollbar[VERTICAL]->getVisible() )
{
- LLRect bottom_scroll_rect = inner_rect_local;
- bottom_scroll_rect.mTop = AUTOSCROLL_SIZE + bottom_scroll_rect.mBottom;
+ LLRect bottom_scroll_rect = screen_local_extents;
+ bottom_scroll_rect.mTop = inner_rect_local.mBottom + auto_scroll_region_height;
if( bottom_scroll_rect.pointInRect( x, y ) && (mScrollbar[VERTICAL]->getDocPos() < mScrollbar[VERTICAL]->getDocPosMax()) )
{
mScrollbar[VERTICAL]->setDocPos( mScrollbar[VERTICAL]->getDocPos() + auto_scroll_speed );
mAutoScrolling = TRUE;
- handled = TRUE;
+ scrolling = true;
}
- LLRect top_scroll_rect = inner_rect_local;
- top_scroll_rect.mBottom = inner_rect_local.mTop - AUTOSCROLL_SIZE;
+ LLRect top_scroll_rect = screen_local_extents;
+ top_scroll_rect.mBottom = inner_rect_local.mTop - auto_scroll_region_height;
if( top_scroll_rect.pointInRect( x, y ) && (mScrollbar[VERTICAL]->getDocPos() > 0) )
{
mScrollbar[VERTICAL]->setDocPos( mScrollbar[VERTICAL]->getDocPos() - auto_scroll_speed );
mAutoScrolling = TRUE;
- handled = TRUE;
+ scrolling = true;
}
}
}
-
- if( !handled )
- {
- handled = childrenHandleDragAndDrop(x, y, mask, drop, cargo_type,
- cargo_data, accept, tooltip_msg) != NULL;
- }
-
- return TRUE;
-}
-
-
-BOOL LLScrollContainer::handleToolTip(S32 x, S32 y, std::string& msg, LLRect* sticky_rect)
-{
- S32 local_x, local_y;
- for( S32 i = 0; i < SCROLLBAR_COUNT; i++ )
- {
- local_x = x - mScrollbar[i]->getRect().mLeft;
- local_y = y - mScrollbar[i]->getRect().mBottom;
- if( mScrollbar[i]->handleToolTip(local_x, local_y, msg, sticky_rect) )
- {
- return TRUE;
- }
- }
- // Handle 'child' view.
- if( mScrolledView )
- {
- local_x = x - mScrolledView->getRect().mLeft;
- local_y = y - mScrolledView->getRect().mBottom;
- if( mScrolledView->handleToolTip(local_x, local_y, msg, sticky_rect) )
- {
- return TRUE;
- }
- }
-
- // Opaque
- return TRUE;
+ return scrolling;
}
void LLScrollContainer::calcVisibleSize( S32 *visible_width, S32 *visible_height, BOOL* show_h_scrollbar, BOOL* show_v_scrollbar ) const
{
- const LLRect& rect = mScrolledView->getRect();
- calcVisibleSize(rect, visible_width, visible_height, show_h_scrollbar, show_v_scrollbar);
-}
-
-void LLScrollContainer::calcVisibleSize( const LLRect& doc_rect, S32 *visible_width, S32 *visible_height, BOOL* show_h_scrollbar, BOOL* show_v_scrollbar ) const
-{
+ const LLRect& doc_rect = getScrolledViewRect();
static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
S32 doc_width = doc_rect.getWidth();
S32 doc_height = doc_rect.getHeight();
- *visible_width = getRect().getWidth() - 2 * mBorder->getBorderWidth();
- *visible_height = getRect().getHeight() - 2 * mBorder->getBorderWidth();
+ S32 border_width = getBorderWidth();
+ *visible_width = getRect().getWidth() - 2 * border_width;
+ *visible_height = getRect().getHeight() - 2 * border_width;
*show_v_scrollbar = FALSE;
- if( *visible_height < doc_height )
- {
- *show_v_scrollbar = TRUE;
- *visible_width -= scrollbar_size;
- }
-
*show_h_scrollbar = FALSE;
- if( *visible_width < doc_width )
- {
- *show_h_scrollbar = TRUE;
- *visible_height -= scrollbar_size;
- // Must retest now that visible_height has changed
- if( !*show_v_scrollbar && (*visible_height < doc_height) )
+ if (!mHideScrollbar)
+ {
+ if( *visible_height < doc_height )
{
*show_v_scrollbar = TRUE;
*visible_width -= scrollbar_size;
}
+
+ if( *visible_width < doc_width )
+ {
+ *show_h_scrollbar = TRUE;
+ *visible_height -= scrollbar_size;
+
+ // Must retest now that visible_height has changed
+ if( !*show_v_scrollbar && (*visible_height < doc_height) )
+ {
+ *show_v_scrollbar = TRUE;
+ *visible_width -= scrollbar_size;
+ }
+ }
}
}
@@ -405,20 +401,20 @@ void LLScrollContainer::draw()
if (mAutoScrolling)
{
// add acceleration to autoscroll
- mAutoScrollRate = llmin(mAutoScrollRate + (LLFrameTimer::getFrameDeltaTimeF32() * AUTO_SCROLL_RATE_ACCEL), MAX_AUTO_SCROLL_RATE);
+ mAutoScrollRate = llmin(mAutoScrollRate + (LLFrameTimer::getFrameDeltaTimeF32() * AUTO_SCROLL_RATE_ACCEL), mMaxAutoScrollRate);
}
else
{
- // reset to minimum
- mAutoScrollRate = MIN_AUTO_SCROLL_RATE;
+ // reset to minimum for next time
+ mAutoScrollRate = mMinAutoScrollRate;
}
- // clear this flag to be set on next call to handleDragAndDrop
+ // clear this flag to be set on next call to autoScroll
mAutoScrolling = FALSE;
// auto-focus when scrollbar active
// this allows us to capture user intent (i.e. stop automatically scrolling the view/etc)
- if (!gFocusMgr.childHasKeyboardFocus(this) &&
- (mScrollbar[VERTICAL]->hasMouseCapture() || mScrollbar[HORIZONTAL]->hasMouseCapture()))
+ if (!hasFocus()
+ && (mScrollbar[VERTICAL]->hasMouseCapture() || mScrollbar[HORIZONTAL]->hasMouseCapture()))
{
focusFirstItem();
}
@@ -445,11 +441,11 @@ void LLScrollContainer::draw()
S32 visible_height = 0;
BOOL show_v_scrollbar = FALSE;
BOOL show_h_scrollbar = FALSE;
- calcVisibleSize( mScrolledView->getRect(), &visible_width, &visible_height, &show_h_scrollbar, &show_v_scrollbar );
+ calcVisibleSize( &visible_width, &visible_height, &show_h_scrollbar, &show_v_scrollbar );
LLLocalClipRect clip(LLRect(mInnerRect.mLeft,
mInnerRect.mBottom + (show_h_scrollbar ? scrollbar_size : 0) + visible_height,
- visible_width,
+ mInnerRect.mRight - (show_v_scrollbar ? scrollbar_size: 0),
mInnerRect.mBottom + (show_h_scrollbar ? scrollbar_size : 0)
));
drawChild(mScrolledView);
@@ -481,31 +477,14 @@ void LLScrollContainer::draw()
sDepth--;
}
}
-
- if (sDebugRects)
- {
- drawDebugRect();
- }
-
- //// *HACK: also draw debug rectangles around currently-being-edited LLView, and any elements that are being highlighted by GUI preview code (see LLFloaterUIPreview)
- //std::set<LLView*>::iterator iter = std::find(sPreviewHighlightedElements.begin(), sPreviewHighlightedElements.end(), this);
- //if ((sEditingUI && this == sEditingUIView) || (iter != sPreviewHighlightedElements.end() && sDrawPreviewHighlights))
- //{
- // drawDebugRect();
- //}
-
} // end draw
bool LLScrollContainer::addChild(LLView* view, S32 tab_group)
{
if (!mScrolledView)
{
- //*TODO: Move LLFolderView to llui and enable this check
-// if (dynamic_cast<LLPanel*>(view) || dynamic_cast<LLContainerView*>(view) || dynamic_cast<LLScrollingPanelList*>(view) || dynamic_cast<LLFolderView*>(view))
- {
- // Use the first panel or container as the scrollable view (bit of a hack)
- mScrolledView = view;
- }
+ // Use the first panel or container as the scrollable view (bit of a hack)
+ mScrolledView = view;
}
bool ret_val = LLView::addChild(view, tab_group);
@@ -517,12 +496,6 @@ bool LLScrollContainer::addChild(LLView* view, S32 tab_group)
return ret_val;
}
-const widget_registry_t& LLScrollContainer::getChildRegistry() const
-{
- // a scroll container can contain any default widget
- return LLDefaultWidgetRegistry::instance();
-}
-
void LLScrollContainer::updateScroll()
{
if (!mScrolledView)
@@ -537,9 +510,9 @@ void LLScrollContainer::updateScroll()
S32 visible_height = 0;
BOOL show_v_scrollbar = FALSE;
BOOL show_h_scrollbar = FALSE;
- calcVisibleSize( doc_rect, &visible_width, &visible_height, &show_h_scrollbar, &show_v_scrollbar );
+ calcVisibleSize( &visible_width, &visible_height, &show_h_scrollbar, &show_v_scrollbar );
- S32 border_width = mBorder->getBorderWidth();
+ S32 border_width = getBorderWidth();
if( show_v_scrollbar )
{
if( doc_rect.mTop < getRect().getHeight() - border_width )
@@ -613,101 +586,116 @@ void LLScrollContainer::updateScroll()
void LLScrollContainer::setBorderVisible(BOOL b)
{
mBorder->setVisible( b );
+ // Recompute inner rect, as border visibility changes it
+ mInnerRect = getLocalRect();
+ mInnerRect.stretch( -getBorderWidth() );
}
-// Scroll so that as much of rect as possible is showing (where rect is defined in the space of scroller view, not scrolled)
-void LLScrollContainer::scrollToShowRect(const LLRect& rect, const LLCoordGL& desired_offset)
+LLRect LLScrollContainer::getVisibleContentRect()
{
- if (!mScrolledView)
- {
- llwarns << "LLScrollContainer::scrollToShowRect with no view!" << llendl;
- return;
- }
+ updateScroll();
+ LLRect visible_rect = getContentWindowRect();
+ LLRect contents_rect = mScrolledView->getRect();
+ visible_rect.translate(-contents_rect.mLeft, -contents_rect.mBottom);
+ return visible_rect;
+}
+LLRect LLScrollContainer::getContentWindowRect()
+{
+ updateScroll();
+ LLRect scroller_view_rect;
S32 visible_width = 0;
S32 visible_height = 0;
- BOOL show_v_scrollbar = FALSE;
BOOL show_h_scrollbar = FALSE;
- const LLRect& scrolled_rect = mScrolledView->getRect();
- calcVisibleSize( scrolled_rect, &visible_width, &visible_height, &show_h_scrollbar, &show_v_scrollbar );
-
- // can't be so far left that right side of rect goes off screen, or so far right that left side does
- S32 horiz_offset = llclamp(desired_offset.mX, llmin(0, -visible_width + rect.getWidth()), 0);
- // can't be so high that bottom of rect goes off screen, or so low that top does
- S32 vert_offset = llclamp(desired_offset.mY, 0, llmax(0, visible_height - rect.getHeight()));
-
- // Vertical
- // 1. First make sure the top is visible
- // 2. Then, if possible without hiding the top, make the bottom visible.
- S32 vert_pos = mScrollbar[VERTICAL]->getDocPos();
-
- // find scrollbar position to get top of rect on screen (scrolling up)
- S32 top_offset = scrolled_rect.mTop - rect.mTop - vert_offset;
- // find scrollbar position to get bottom of rect on screen (scrolling down)
- S32 bottom_offset = vert_offset == 0 ? scrolled_rect.mTop - rect.mBottom - visible_height : top_offset;
- // scroll up far enough to see top or scroll down just enough if item is bigger than visual area
- if( vert_pos >= top_offset || visible_height < rect.getHeight())
- {
- vert_pos = top_offset;
- }
- // else scroll down far enough to see bottom
- else
- if( vert_pos <= bottom_offset )
+ BOOL show_v_scrollbar = FALSE;
+ calcVisibleSize( &visible_width, &visible_height, &show_h_scrollbar, &show_v_scrollbar );
+ S32 border_width = getBorderWidth();
+ scroller_view_rect.setOriginAndSize(border_width,
+ show_h_scrollbar ? mScrollbar[HORIZONTAL]->getRect().mTop : border_width,
+ visible_width,
+ visible_height);
+ return scroller_view_rect;
+}
+
+// rect is in document coordinates, constraint is in display coordinates relative to content window rect
+void LLScrollContainer::scrollToShowRect(const LLRect& rect, const LLRect& constraint)
+{
+ if (!mScrolledView)
{
- vert_pos = bottom_offset;
+ llwarns << "LLScrollContainer::scrollToShowRect with no view!" << llendl;
+ return;
}
+ LLRect content_window_rect = getContentWindowRect();
+ // get document rect
+ LLRect scrolled_rect = mScrolledView->getRect();
+
+ // shrink target rect to fit within constraint region, biasing towards top left
+ LLRect rect_to_constrain = rect;
+ rect_to_constrain.mBottom = llmax(rect_to_constrain.mBottom, rect_to_constrain.mTop - constraint.getHeight());
+ rect_to_constrain.mRight = llmin(rect_to_constrain.mRight, rect_to_constrain.mLeft + constraint.getWidth());
+
+ // calculate allowable positions for scroller window in document coordinates
+ LLRect allowable_scroll_rect(rect_to_constrain.mRight - constraint.mRight,
+ rect_to_constrain.mBottom - constraint.mBottom,
+ rect_to_constrain.mLeft - constraint.mLeft,
+ rect_to_constrain.mTop - constraint.mTop);
+
+ // translate from allowable region for lower left corner to upper left corner
+ allowable_scroll_rect.translate(0, content_window_rect.getHeight());
+
+ S32 vert_pos = llclamp(mScrollbar[VERTICAL]->getDocPos(),
+ mScrollbar[VERTICAL]->getDocSize() - allowable_scroll_rect.mTop, // min vertical scroll
+ mScrollbar[VERTICAL]->getDocSize() - allowable_scroll_rect.mBottom); // max vertical scroll
+
mScrollbar[VERTICAL]->setDocSize( scrolled_rect.getHeight() );
- mScrollbar[VERTICAL]->setPageSize( visible_height );
+ mScrollbar[VERTICAL]->setPageSize( content_window_rect.getHeight() );
mScrollbar[VERTICAL]->setDocPos( vert_pos );
- // Horizontal
- // 1. First make sure left side is visible
- // 2. Then, if possible without hiding the left side, make the right side visible.
- S32 horiz_pos = mScrollbar[HORIZONTAL]->getDocPos();
- S32 left_offset = rect.mLeft - scrolled_rect.mLeft + horiz_offset;
- S32 right_offset = horiz_offset == 0 ? rect.mRight - scrolled_rect.mLeft - visible_width : left_offset;
+ S32 horizontal_pos = llclamp(mScrollbar[HORIZONTAL]->getDocPos(),
+ allowable_scroll_rect.mLeft,
+ allowable_scroll_rect.mRight);
- if( horiz_pos >= left_offset || visible_width < rect.getWidth() )
- {
- horiz_pos = left_offset;
- }
- else if( horiz_pos <= right_offset )
- {
- horiz_pos = right_offset;
- }
-
mScrollbar[HORIZONTAL]->setDocSize( scrolled_rect.getWidth() );
- mScrollbar[HORIZONTAL]->setPageSize( visible_width );
- mScrollbar[HORIZONTAL]->setDocPos( horiz_pos );
+ mScrollbar[HORIZONTAL]->setPageSize( content_window_rect.getWidth() );
+ mScrollbar[HORIZONTAL]->setDocPos( horizontal_pos );
// propagate scroll to document
updateScroll();
+
+ // In case we are in accordion tab notify parent to show selected rectangle
+ LLRect screen_rc;
+ localRectToScreen(rect_to_constrain, &screen_rc);
+ notifyParent(LLSD().with("scrollToShowRect",screen_rc.getValue()));
}
void LLScrollContainer::pageUp(S32 overlap)
{
mScrollbar[VERTICAL]->pageUp(overlap);
+ updateScroll();
}
void LLScrollContainer::pageDown(S32 overlap)
{
mScrollbar[VERTICAL]->pageDown(overlap);
+ updateScroll();
}
void LLScrollContainer::goToTop()
{
mScrollbar[VERTICAL]->setDocPos(0);
+ updateScroll();
}
void LLScrollContainer::goToBottom()
{
mScrollbar[VERTICAL]->setDocPos(mScrollbar[VERTICAL]->getDocSize());
+ updateScroll();
}
S32 LLScrollContainer::getBorderWidth() const
{
- if (mBorder)
+ if (mBorder->getVisible())
{
return mBorder->getBorderWidth();
}
diff --git a/indra/llui/llscrollcontainer.h b/indra/llui/llscrollcontainer.h
index 26d8cc824e..46a71a7e30 100644
--- a/indra/llui/llscrollcontainer.h
+++ b/indra/llui/llscrollcontainer.h
@@ -2,31 +2,25 @@
* @file llscrollcontainer.h
* @brief LLScrollContainer class header file.
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -53,6 +47,10 @@ class LLUICtrlFactory;
* the width and height of the view you're scrolling.
*
*****************************************************************************/
+
+struct ScrollContainerRegistry : public LLChildRegistry<ScrollContainerRegistry>
+{};
+
class LLScrollContainer : public LLUICtrl
{
public:
@@ -62,12 +60,21 @@ public:
struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
{
- Optional<bool> is_opaque;
+ Optional<bool> is_opaque,
+ reserve_scroll_corner,
+ border_visible,
+ hide_scrollbar;
+ Optional<F32> min_auto_scroll_rate,
+ max_auto_scroll_rate;
Optional<LLUIColor> bg_color;
- Optional<bool> reserve_scroll_corner;
+ Optional<LLScrollbar::callback_t> scroll_callback;
Params();
};
+
+ // my valid children are stored in this registry
+ typedef ScrollContainerRegistry child_registry_t;
+
protected:
LLScrollContainer(const Params&);
friend class LLUICtrlFactory;
@@ -76,21 +83,23 @@ public:
virtual void setValue(const LLSD& value) { mInnerRect.setValue(value); }
- void calcVisibleSize( S32 *visible_width, S32 *visible_height, BOOL* show_h_scrollbar, BOOL* show_v_scrollbar ) const;
- void calcVisibleSize( const LLRect& doc_rect, S32 *visible_width, S32 *visible_height, BOOL* show_h_scrollbar, BOOL* show_v_scrollbar ) const;
void setBorderVisible( BOOL b );
- void scrollToShowRect( const LLRect& rect, const LLCoordGL& desired_offset );
+ void scrollToShowRect( const LLRect& rect, const LLRect& constraint);
+ void scrollToShowRect( const LLRect& rect) { scrollToShowRect(rect, LLRect(0, mInnerRect.getHeight(), mInnerRect.getWidth(), 0)); }
+
void setReserveScrollCorner( BOOL b ) { mReserveScrollCorner = b; }
- const LLRect& getScrolledViewRect() const { return mScrolledView->getRect(); }
+ LLRect getVisibleContentRect();
+ LLRect getContentWindowRect();
+ const LLRect& getScrolledViewRect() const { return mScrolledView ? mScrolledView->getRect() : LLRect::null; }
void pageUp(S32 overlap = 0);
void pageDown(S32 overlap = 0);
void goToTop();
void goToBottom();
+ bool isAtTop() { return mScrollbar[VERTICAL]->isAtBeginning(); }
+ bool isAtBottom() { return mScrollbar[VERTICAL]->isAtEnd(); }
S32 getBorderWidth() const;
- BOOL needsToScroll(S32 x, S32 y, SCROLL_ORIENTATION axis) const;
-
// LLView functionality
virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
virtual BOOL handleKeyHere(KEY key, MASK mask);
@@ -101,16 +110,17 @@ public:
EAcceptance* accept,
std::string& tooltip_msg);
- virtual BOOL handleToolTip(S32 x, S32 y, std::string& msg, LLRect* sticky_rect);
virtual void draw();
virtual bool addChild(LLView* view, S32 tab_group = 0);
- virtual const widget_registry_t& getChildRegistry() const;
+
+ bool autoScroll(S32 x, S32 y);
private:
// internal scrollbar handlers
virtual void scrollHorizontal( S32 new_pos );
virtual void scrollVertical( S32 new_pos );
void updateScroll();
+ void calcVisibleSize( S32 *visible_width, S32 *visible_height, BOOL* show_h_scrollbar, BOOL* show_v_scrollbar ) const;
LLScrollbar* mScrollbar[SCROLLBAR_COUNT];
LLView* mScrolledView;
@@ -122,6 +132,9 @@ private:
BOOL mReserveScrollCorner;
BOOL mAutoScrolling;
F32 mAutoScrollRate;
+ F32 mMinAutoScrollRate;
+ F32 mMaxAutoScrollRate;
+ bool mHideScrollbar;
};
diff --git a/indra/llui/llscrollingpanellist.cpp b/indra/llui/llscrollingpanellist.cpp
index 1f3a7f9fcf..9b65c2b79d 100644
--- a/indra/llui/llscrollingpanellist.cpp
+++ b/indra/llui/llscrollingpanellist.cpp
@@ -2,31 +2,25 @@
* @file llscrollingpanellist.cpp
* @brief
*
- * $LicenseInfo:firstyear=2006&license=viewergpl$
- *
- * Copyright (c) 2006-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2006&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -35,7 +29,7 @@
#include "llscrollingpanellist.h"
-static LLDefaultWidgetRegistry::Register<LLScrollingPanelList> r("scrolling_panel_list");
+static LLDefaultChildRegistry::Register<LLScrollingPanelList> r("scrolling_panel_list");
/////////////////////////////////////////////////////////////////////
@@ -47,16 +41,19 @@ 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());
}
-void LLScrollingPanelList::addPanel( LLScrollingPanel* panel )
+S32 LLScrollingPanelList::addPanel( LLScrollingPanel* panel )
{
addChildInBack( panel );
mPanelList.push_front( panel );
- const S32 GAP_BETWEEN_PANELS = 6;
-
// Resize this view
S32 total_height = 0;
S32 max_width = 0;
@@ -69,7 +66,11 @@ void 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;
@@ -81,6 +82,29 @@ void LLScrollingPanelList::addPanel( LLScrollingPanel* panel )
childp->translate( -childp->getRect().mLeft, cur_y - childp->getRect().mBottom);
cur_y -= GAP_BETWEEN_PANELS;
}
+
+ return total_height;
+}
+
+void LLScrollingPanelList::removePanel(LLScrollingPanel* panel)
+{
+ U32 index = 0;
+ LLScrollingPanelList::panel_list_t::const_iterator iter;
+
+ if (!mPanelList.empty())
+ {
+ for (iter = mPanelList.begin(); iter != mPanelList.end(); ++iter, ++index)
+ {
+ if (*iter == panel)
+ {
+ break;
+ }
+ }
+ if(iter != mPanelList.end())
+ {
+ removePanel(index);
+ }
+ }
}
void LLScrollingPanelList::removePanel( U32 panel_index )
@@ -110,7 +134,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;
@@ -179,3 +207,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 5dc23facda..8f569c2a58 100644
--- a/indra/llui/llscrollingpanellist.h
+++ b/indra/llui/llscrollingpanellist.h
@@ -1,31 +1,25 @@
/**
* @file llscrollingpanellist.h
*
- * $LicenseInfo:firstyear=2006&license=viewergpl$
- *
- * Copyright (c) 2006-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2006&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -61,13 +55,14 @@ public:
Params()
{
name = "scrolling_panel_list";
- follows.flags = FOLLOWS_LEFT | FOLLOWS_BOTTOM;
}
};
LLScrollingPanelList(const Params& p)
: LLUICtrl(p)
{}
+ static const S32 GAP_BETWEEN_PANELS = 6;
+
typedef std::deque<LLScrollingPanel*> panel_list_t;
virtual void setValue(const LLSD& value) {};
@@ -75,7 +70,8 @@ public:
virtual void draw();
void clearPanels();
- void addPanel( LLScrollingPanel* panel );
+ S32 addPanel( LLScrollingPanel* panel );
+ void removePanel( LLScrollingPanel* panel );
void removePanel( U32 panel_index );
void updatePanels(BOOL allow_modify);
const panel_list_t& getPanelList() { return mPanelList; }
@@ -83,6 +79,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 4e6de24160..9d25c7180d 100644
--- a/indra/llui/llscrolllistcell.cpp
+++ b/indra/llui/llscrolllistcell.cpp
@@ -3,31 +3,25 @@
* @brief Scroll lists are composed of rows (items), each of which
* contains columns (cells).
*
- * $LicenseInfo:firstyear=2007&license=viewergpl$
- *
- * Copyright (c) 2007-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -71,7 +65,8 @@ LLScrollListCell* LLScrollListCell::create(const LLScrollListCell::Params& cell_
LLScrollListCell::LLScrollListCell(const LLScrollListCell::Params& p)
-: mWidth(p.width)
+: mWidth(p.width),
+ mToolTip(p.tool_tip)
{}
// virtual
@@ -177,7 +172,6 @@ LLScrollListText::LLScrollListText(const LLScrollListCell::Params& p)
mFont(p.font),
mColor(p.color),
mUseColor(p.color.isProvided()),
- mFontStyle(LLFontGL::NORMAL),
mFontAlignment(p.font_halign),
mVisible(p.visible),
mHighlightCount( 0 ),
@@ -185,10 +179,12 @@ LLScrollListText::LLScrollListText(const LLScrollListCell::Params& p)
{
sCount++;
+ mTextWidth = getWidth();
+
// initialize rounded rect image
if (!mRoundedRectImage)
{
- mRoundedRectImage = LLUI::getUIImage("rounded_square.tga");
+ mRoundedRectImage = LLUI::getUIImage("Rounded_Square");
}
}
@@ -205,6 +201,28 @@ BOOL LLScrollListText::isText() const
return TRUE;
}
+// virtual
+const std::string &LLScrollListText::getToolTip() const
+{
+ // If base class has a tooltip, return that
+ if (! LLScrollListCell::getToolTip().empty())
+ return LLScrollListCell::getToolTip();
+
+ // ...otherwise, return the value itself as the tooltip
+ return mText.getString();
+}
+
+// virtual
+BOOL LLScrollListText::needsToolTip() const
+{
+ // If base class has a tooltip, return that
+ if (LLScrollListCell::needsToolTip())
+ return LLScrollListCell::needsToolTip();
+
+ // ...otherwise, show tooltips for truncated text
+ return mFont->getWidth(mText.getString()) > getWidth();
+}
+
//virtual
BOOL LLScrollListText::getVisible() const
{
@@ -240,6 +258,13 @@ void LLScrollListText::setText(const LLStringExplicit& text)
mText = text;
}
+void LLScrollListText::setFontStyle(const U8 font_style)
+{
+ LLFontDescriptor new_desc(mFont->getFontDesc());
+ new_desc.setStyle(font_style);
+ mFont = LLFontGL::getFont(new_desc);
+}
+
//virtual
void LLScrollListText::setValue(const LLSD& text)
{
@@ -304,17 +329,16 @@ void LLScrollListText::draw(const LLColor4& color, const LLColor4& highlight_col
break;
}
mFont->render(mText.getWString(), 0,
- start_x, 2.f,
- display_color,
- mFontAlignment,
- LLFontGL::BOTTOM,
- mFontStyle,
- LLFontGL::NO_SHADOW,
- string_chars,
- getWidth(),
- &right_x,
- FALSE,
- TRUE);
+ start_x, 2.f,
+ display_color,
+ mFontAlignment,
+ LLFontGL::BOTTOM,
+ 0,
+ LLFontGL::NO_SHADOW,
+ string_chars,
+ getTextWidth(),
+ &right_x,
+ TRUE);
}
//
@@ -325,7 +349,7 @@ LLScrollListCheck::LLScrollListCheck(const LLScrollListCell::Params& p)
{
LLCheckBoxCtrl::Params checkbox_p;
checkbox_p.name("checkbox");
- checkbox_p.rect.left(0).bottom(0).width(p.width).height(p.width);
+ checkbox_p.rect = LLRect(0, p.width, p.width, 0);
checkbox_p.enabled(p.enabled);
checkbox_p.initial_value(p.value());
diff --git a/indra/llui/llscrolllistcell.h b/indra/llui/llscrolllistcell.h
index 2ab13f7618..d625ebddcc 100644
--- a/indra/llui/llscrolllistcell.h
+++ b/indra/llui/llscrolllistcell.h
@@ -3,31 +3,25 @@
* @brief Scroll lists are composed of rows (items), each of which
* contains columns (cells).
*
- * $LicenseInfo:firstyear=2007&license=viewergpl$
- *
- * Copyright (c) 2007-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -66,6 +60,7 @@ public:
Optional<void*> userdata;
Optional<LLSD> value;
+ Optional<std::string> tool_tip;
Optional<const LLFontGL*> font;
Optional<LLColor4> font_color;
@@ -80,6 +75,7 @@ public:
enabled("enabled", true),
visible("visible", true),
value("value"),
+ tool_tip("tool_tip", ""),
font("font", LLFontGL::getFontSansSerifSmall()),
font_color("font_color", LLColor4::black),
color("color", LLColor4::white),
@@ -94,16 +90,20 @@ public:
LLScrollListCell(const LLScrollListCell::Params&);
virtual ~LLScrollListCell() {};
- virtual void draw(const LLColor4& color, const LLColor4& highlight_color) const = 0; // truncate to given width, if possible
+
+ virtual void draw(const LLColor4& color, const LLColor4& highlight_color) const {}; // truncate to given width, if possible
virtual S32 getWidth() const {return mWidth;}
virtual S32 getContentWidth() const { return 0; }
- virtual S32 getHeight() const = 0;
+ virtual S32 getHeight() const { return 0; }
virtual const LLSD getValue() const;
virtual void setValue(const LLSD& value) { }
+ virtual const std::string &getToolTip() const { return mToolTip; }
+ virtual void setToolTip(const std::string &str) { mToolTip = str; }
virtual BOOL getVisible() const { return TRUE; }
virtual void setWidth(S32 width) { mWidth = width; }
virtual void highlightText(S32 offset, S32 num_chars) {}
- virtual BOOL isText() const = 0;
+ virtual BOOL isText() const { return FALSE; }
+ virtual BOOL needsToolTip() const { return ! mToolTip.empty(); }
virtual void setColor(const LLColor4&) {}
virtual void onCommit() {};
@@ -112,6 +112,7 @@ public:
private:
S32 mWidth;
+ std::string mToolTip;
};
class LLScrollListSpacer : public LLScrollListCell
@@ -120,8 +121,6 @@ public:
LLScrollListSpacer(const LLScrollListCell::Params& p) : LLScrollListCell(p) {}
/*virtual*/ ~LLScrollListSpacer() {};
/*virtual*/ void draw(const LLColor4& color, const LLColor4& highlight_color) const {}
- /*virtual*/ S32 getHeight() const { return 0; }
- /*virtual*/ BOOL isText() const { return FALSE; }
};
/*
@@ -143,16 +142,22 @@ public:
/*virtual*/ void setColor(const LLColor4&);
/*virtual*/ BOOL isText() const;
+ /*virtual*/ const std::string & getToolTip() const;
+ /*virtual*/ BOOL needsToolTip() const;
+
+ S32 getTextWidth() const { return mTextWidth;}
+ void setTextWidth(S32 value) { mTextWidth = value;}
+ virtual void setWidth(S32 width) { LLScrollListCell::setWidth(width); mTextWidth = width; }
void setText(const LLStringExplicit& text);
- void setFontStyle(const U8 font_style) { mFontStyle = font_style; }
+ void setFontStyle(const U8 font_style);
private:
LLUIString mText;
+ S32 mTextWidth;
const LLFontGL* mFont;
LLColor4 mColor;
U8 mUseColor;
- U8 mFontStyle;
LLFontGL::HAlign mFontAlignment;
BOOL mVisible;
S32 mHighlightCount;
@@ -176,7 +181,6 @@ public:
/*virtual*/ S32 getHeight() const;
/*virtual*/ const LLSD getValue() const;
/*virtual*/ void setColor(const LLColor4&);
- /*virtual*/ BOOL isText()const { return FALSE; }
/*virtual*/ void setValue(const LLSD& value);
private:
@@ -203,7 +207,6 @@ public:
/*virtual*/ void setEnabled(BOOL enable);
LLCheckBoxCtrl* getCheckBox() { return mCheckBox; }
- /*virtual*/ BOOL isText() const { return FALSE; }
private:
LLCheckBoxCtrl* mCheckBox;
diff --git a/indra/llui/llscrolllistcolumn.cpp b/indra/llui/llscrolllistcolumn.cpp
index 48fddbfb71..2a4c1ca44c 100644
--- a/indra/llui/llscrolllistcolumn.cpp
+++ b/indra/llui/llscrolllistcolumn.cpp
@@ -3,31 +3,25 @@
* @brief Scroll lists are composed of rows (items), each of which
* contains columns (cells).
*
- * $LicenseInfo:firstyear=2007&license=viewergpl$
- *
- * Copyright (c) 2007-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -44,9 +38,19 @@
const S32 MIN_COLUMN_WIDTH = 20;
+// defaults for LLScrollColumnHeader param block pulled from widgets/scroll_column_header.xml
+static LLWidgetNameRegistry::StaticRegistrar sRegisterColumnHeaderParams(&typeid(LLScrollColumnHeader::Params), "scroll_column_header");
+
//---------------------------------------------------------------------------
// LLScrollColumnHeader
//---------------------------------------------------------------------------
+LLScrollColumnHeader::Params::Params()
+: column("column")
+{
+ name = "column_header";
+ tab_stop(false);
+}
+
LLScrollColumnHeader::LLScrollColumnHeader(const LLScrollColumnHeader::Params& p)
: LLButton(p), // use combobox params to steal images
@@ -65,8 +69,6 @@ LLScrollColumnHeader::LLScrollColumnHeader(const LLScrollColumnHeader::Params& p
resize_bar_p.enabled(false);
mResizeBar = LLUICtrlFactory::create<LLResizeBar>(resize_bar_p);
addChild(mResizeBar);
-
- setToolTip(p.label());
}
LLScrollColumnHeader::~LLScrollColumnHeader()
@@ -285,6 +287,16 @@ void LLScrollListColumn::SortNames::declareValues()
declare("descending", LLScrollListColumn::DESCENDING);
}
+//
+// LLScrollListColumn
+//
+//static
+const LLScrollListColumn::Params& LLScrollListColumn::getDefaultParams()
+{
+ return LLUICtrlFactory::getDefaultParams<LLScrollListColumn>();
+}
+
+
LLScrollListColumn::LLScrollListColumn(const Params& p, LLScrollListCtrl* parent)
: mWidth(0),
mIndex (-1),
diff --git a/indra/llui/llscrolllistcolumn.h b/indra/llui/llscrolllistcolumn.h
index c1bb86577f..e2711ac75a 100644
--- a/indra/llui/llscrolllistcolumn.h
+++ b/indra/llui/llscrolllistcolumn.h
@@ -3,31 +3,25 @@
* @brief Scroll lists are composed of rows (items), each of which
* contains columns (cells).
*
- * $LicenseInfo:firstyear=2007&license=viewergpl$
- *
- * Copyright (c) 2007-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -50,20 +44,7 @@ public:
{
Mandatory<LLScrollListColumn*> column;
- Params()
- : column("column")
- {
- name = "column_header";
- image_unselected.name("square_btn_32x128.tga");
- image_selected.name("square_btn_selected_32x128.tga");
- image_disabled.name("square_btn_32x128.tga");
- image_disabled_selected.name("square_btn_selected_32x128.tga");
- image_overlay.name("combobox_arrow.tga");
- image_overlay_alignment("right");
- font_halign = LLFontGL::LEFT;
- tab_stop(false);
- scale_image(true);
- }
+ Params();
};
LLScrollColumnHeader(const Params&);
~LLScrollColumnHeader();
@@ -116,12 +97,12 @@ public:
struct Width : public LLInitParam::Choice<Width>
{
- Option<bool> dynamic_width;
- Option<S32> pixel_width;
- Option<F32> relative_width;
+ Alternative<bool> dynamic_width;
+ Alternative<S32> pixel_width;
+ Alternative<F32> relative_width;
Width()
- : dynamic_width("dynamicwidth", false),
+ : dynamic_width("dynamic_width", false),
pixel_width("width"),
relative_width("relative_width", -1.f)
{
@@ -133,8 +114,8 @@ public:
// either an image or label is used in column header
struct Header : public LLInitParam::Choice<Header>
{
- Option<std::string> label;
- Option<LLUIImage*> image;
+ Alternative<std::string> label;
+ Alternative<LLUIImage*> image;
Header()
: label("label"),
@@ -160,8 +141,10 @@ public:
}
};
+ static const Params& getDefaultParams();
+
//NOTE: this is default constructible so we can store it in a map.
- LLScrollListColumn(const Params& p = Params(), LLScrollListCtrl* = NULL);
+ LLScrollListColumn(const Params& p = getDefaultParams(), LLScrollListCtrl* = NULL);
void setWidth(S32 width);
S32 getWidth() const { return mWidth; }
diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp
index 42c7c892c8..84e438cfb7 100644
--- a/indra/llui/llscrolllistctrl.cpp
+++ b/indra/llui/llscrolllistctrl.cpp
@@ -3,36 +3,28 @@
* @brief Scroll lists are composed of rows (items), each of which
* contains columns (cells).
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
-#define INSTANTIATE_GETCHILD_SCROLLLIST
-
#include "linden_common.h"
#include "llscrolllistctrl.h"
@@ -46,6 +38,8 @@
#include "llcheckboxctrl.h"
#include "llclipboard.h"
#include "llfocusmgr.h"
+#include "llgl.h" // LLGLSUIDefault()
+#include "lllocalcliprect.h"
//#include "llrender.h"
#include "llresmgr.h"
#include "llscrollbar.h"
@@ -59,16 +53,21 @@
#include "llviewborder.h"
#include "lltextbox.h"
#include "llsdparam.h"
+#include "llcachename.h"
+#include "llmenugl.h"
+#include "llurlaction.h"
+#include "lltooltip.h"
-template LLScrollListCtrl* LLView::getChild<LLScrollListCtrl>( const std::string& name, BOOL recurse, BOOL create_if_missing ) const;
+#include <boost/bind.hpp>
-static LLDefaultWidgetRegistry::Register<LLScrollListCtrl> r("scroll_list");
+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)
@@ -81,12 +80,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!
@@ -96,8 +103,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;
};
@@ -106,11 +115,11 @@ struct SortScrollListItem
//---------------------------------------------------------------------------
LLScrollListCtrl::Contents::Contents()
-: columns("columns"),
- rows("rows")
+: columns("column"),
+ rows("row")
{
- addSynonym(columns, "column");
- addSynonym(rows, "row");
+ addSynonym(columns, "columns");
+ addSynonym(rows, "rows");
}
LLScrollListCtrl::Params::Params()
@@ -120,8 +129,10 @@ LLScrollListCtrl::Params::Params()
search_column("search_column", 0),
sort_column("sort_column", -1),
sort_ascending("sort_ascending", true),
+ mouse_wheel_opaque("mouse_wheel_opaque", false),
commit_on_keyboard_movement("commit_on_keyboard_movement", true),
heading_height("heading_height"),
+ page_lines("page_lines", 0),
background_visible("background_visible"),
draw_stripes("draw_stripes"),
column_padding("column_padding"),
@@ -130,10 +141,14 @@ LLScrollListCtrl::Params::Params()
bg_selected_color("bg_selected_color"),
fg_disable_color("fg_disable_color"),
bg_writeable_color("bg_writeable_color"),
- bg_read_only_color("bg_read_only_color"),
+ bg_readonly_color("bg_readonly_color"),
bg_stripe_color("bg_stripe_color"),
hovered_color("hovered_color"),
- highlighted_color("highlighted_color")
+ highlighted_color("highlighted_color"),
+ contents(""),
+ scroll_bar_bg_visible("scroll_bar_bg_visible"),
+ scroll_bar_bg_color("scroll_bar_bg_color")
+ , border("border")
{
name = "scroll_list";
mouse_opaque = true;
@@ -143,7 +158,8 @@ LLScrollListCtrl::LLScrollListCtrl(const LLScrollListCtrl::Params& p)
: LLUICtrl(p),
mLineHeight(0),
mScrollLines(0),
- mPageLines(0),
+ mMouseWheelOpaque(p.mouse_wheel_opaque),
+ mPageLines(p.page_lines),
mMaxSelectable(0),
mAllowKeyboardMovement(TRUE),
mCommitOnKeyboardMovement(p.commit_on_keyboard_movement),
@@ -160,13 +176,14 @@ LLScrollListCtrl::LLScrollListCtrl(const LLScrollListCtrl::Params& p)
mOnSortChangedCallback( NULL ),
mHighlightedItem(-1),
mBorder(NULL),
+ mSortCallback(NULL),
+ mPopupMenu(NULL),
mNumDynamicWidthColumns(0),
mTotalStaticColumnWidth(0),
mTotalColumnPadding(0),
- mSorted(FALSE),
+ mSorted(false),
mDirty(FALSE),
mOriginalSelection(-1),
- mDrewSelected(FALSE),
mLastSelected(NULL),
mHeadingHeight(p.heading_height),
mAllowMultipleSelection(p.multi_select),
@@ -174,7 +191,7 @@ LLScrollListCtrl::LLScrollListCtrl(const LLScrollListCtrl::Params& p)
mBackgroundVisible(p.background_visible),
mDrawStripes(p.draw_stripes),
mBgWriteableColor(p.bg_writeable_color()),
- mBgReadOnlyColor(p.bg_read_only_color()),
+ mBgReadOnlyColor(p.bg_readonly_color()),
mBgSelectedColor(p.bg_selected_color()),
mBgStripeColor(p.bg_stripe_color()),
mFgSelectedColor(p.fg_selected_color()),
@@ -183,7 +200,8 @@ LLScrollListCtrl::LLScrollListCtrl(const LLScrollListCtrl::Params& p)
mHighlightedColor(p.highlighted_color()),
mHoveredColor(p.hovered_color()),
mSearchColumn(p.search_column),
- mColumnPadding(p.column_padding)
+ mColumnPadding(p.column_padding),
+ mContextMenuType(MENU_NONE)
{
mItemListRect.setOriginAndSize(
mBorderThickness,
@@ -193,8 +211,6 @@ LLScrollListCtrl::LLScrollListCtrl(const LLScrollListCtrl::Params& p)
updateLineHeight();
- mPageLines = mLineHeight? (mItemListRect.getHeight()) / mLineHeight : 0;
-
// Init the scrollbar
static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
@@ -211,10 +227,12 @@ LLScrollListCtrl::LLScrollListCtrl(const LLScrollListCtrl::Params& p)
sbparams.orientation(LLScrollbar::VERTICAL);
sbparams.doc_size(getItemCount());
sbparams.doc_pos(mScrollLines);
- sbparams.page_size(mPageLines);
+ sbparams.page_size( getLinesPerPage() );
sbparams.change_callback(boost::bind(&LLScrollListCtrl::onScrollChange, this, _1, _2));
sbparams.follows.flags(FOLLOWS_RIGHT | FOLLOWS_TOP | FOLLOWS_BOTTOM);
sbparams.visible(false);
+ sbparams.bg_visible(p.scroll_bar_bg_visible);
+ sbparams.bg_color(p.scroll_bar_bg_color);
mScrollbar = LLUICtrlFactory::create<LLScrollbar> (sbparams);
addChild(mScrollbar);
@@ -222,10 +240,8 @@ LLScrollListCtrl::LLScrollListCtrl(const LLScrollListCtrl::Params& p)
if (p.has_border)
{
LLRect border_rect = getLocalRect();
- LLViewBorder::Params params;
- params.name("dig border");
+ LLViewBorder::Params params = p.border;
params.rect(border_rect);
- params.bevel_type(LLViewBorder::BEVEL_IN);
mBorder = LLUICtrlFactory::create<LLViewBorder> (params);
addChild(mBorder);
}
@@ -262,6 +278,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));
}
@@ -301,12 +319,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());
}
@@ -378,6 +393,10 @@ std::vector<LLScrollListItem*> LLScrollListCtrl::getAllSelected() const
S32 LLScrollListCtrl::getFirstSelectedIndex() const
{
S32 CurSelectedIndex = 0;
+
+ // make sure sort is up to date before returning an index
+ updateSort();
+
item_list::const_iterator iter;
for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
{
@@ -462,8 +481,9 @@ void LLScrollListCtrl::updateLayout()
getChildView("comment_text")->setShape(mItemListRect);
// how many lines of content in a single "page"
- mPageLines = mLineHeight? mItemListRect.getHeight() / mLineHeight : 0;
- BOOL scrollbar_visible = getItemCount() > mPageLines;
+ S32 page_lines = getLinesPerPage();
+
+ BOOL scrollbar_visible = mLineHeight * getItemCount() > mItemListRect.getHeight();
if (scrollbar_visible)
{
// provide space on the right for scrollbar
@@ -472,7 +492,7 @@ void LLScrollListCtrl::updateLayout()
mScrollbar->setOrigin(getRect().getWidth() - mBorderThickness - scrollbar_size, mItemListRect.mBottom);
mScrollbar->reshape(scrollbar_size, mItemListRect.getHeight() + (mDisplayColumnHeaders ? mHeadingHeight : 0));
- mScrollbar->setPageSize( mPageLines );
+ mScrollbar->setPageSize(page_lines);
mScrollbar->setDocSize( getItemCount() );
mScrollbar->setVisible(scrollbar_visible);
@@ -484,6 +504,9 @@ void LLScrollListCtrl::updateLayout()
void LLScrollListCtrl::fitContents(S32 max_width, S32 max_height)
{
S32 height = llmin( getRequiredRect().getHeight(), max_height );
+ if(mPageLines)
+ height = llmin( mPageLines * mLineHeight + 2*mBorderThickness + (mDisplayColumnHeaders ? mHeadingHeight : 0), height );
+
S32 width = getRect().getWidth();
reshape( width, height );
@@ -511,7 +534,7 @@ BOOL LLScrollListCtrl::addItem( LLScrollListItem* item, EAddPosition pos, BOOL r
{
case ADD_TOP:
mItemList.push_front(item);
- setSorted(FALSE);
+ setNeedsSort();
break;
case ADD_SORTED:
@@ -524,22 +547,22 @@ 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
- setSorted(FALSE);
+ setNeedsSort();
break;
}
case ADD_BOTTOM:
mItemList.push_back(item);
- setSorted(FALSE);
+ setNeedsSort();
break;
default:
llassert(0);
mItemList.push_back(item);
- setSorted(FALSE);
+ setNeedsSort();
break;
}
@@ -714,6 +737,12 @@ void LLScrollListCtrl::setHeadingHeight(S32 heading_height)
updateLayout();
}
+void LLScrollListCtrl::setPageLines(S32 new_page_lines)
+{
+ mPageLines = new_page_lines;
+
+ updateLayout();
+}
BOOL LLScrollListCtrl::selectFirstItem()
{
@@ -763,6 +792,9 @@ BOOL LLScrollListCtrl::selectItemRange( S32 first_index, S32 last_index )
return FALSE;
}
+ // make sure sort is up to date
+ updateSort();
+
S32 listlen = (S32)mItemList.size();
first_index = llclamp(first_index, 0, listlen-1);
@@ -816,6 +848,7 @@ void LLScrollListCtrl::swapWithNext(S32 index)
// At end of list, doesn't do anything
return;
}
+ updateSort();
LLScrollListItem *cur_itemp = mItemList[index];
mItemList[index] = mItemList[index + 1];
mItemList[index + 1] = cur_itemp;
@@ -829,6 +862,7 @@ void LLScrollListCtrl::swapWithPrevious(S32 index)
// At beginning of list, don't do anything
}
+ updateSort();
LLScrollListItem *cur_itemp = mItemList[index];
mItemList[index] = mItemList[index - 1];
mItemList[index - 1] = cur_itemp;
@@ -842,6 +876,8 @@ void LLScrollListCtrl::deleteSingleItem(S32 target_index)
return;
}
+ updateSort();
+
LLScrollListItem *itemp;
itemp = mItemList[target_index];
if (itemp == mLastSelected)
@@ -914,14 +950,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)))
@@ -943,6 +979,8 @@ S32 LLScrollListCtrl::selectMultiple( std::vector<LLUUID> ids )
S32 LLScrollListCtrl::getItemIndex( LLScrollListItem* target_item ) const
{
+ updateSort();
+
S32 index = 0;
item_list::const_iterator iter;
for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
@@ -959,6 +997,8 @@ S32 LLScrollListCtrl::getItemIndex( LLScrollListItem* target_item ) const
S32 LLScrollListCtrl::getItemIndex( const LLUUID& target_id ) const
{
+ updateSort();
+
S32 index = 0;
item_list::const_iterator iter;
for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
@@ -984,6 +1024,8 @@ void LLScrollListCtrl::selectPrevItem( BOOL extend_selection)
}
else
{
+ updateSort();
+
item_list::iterator iter;
for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
{
@@ -1026,6 +1068,8 @@ void LLScrollListCtrl::selectNextItem( BOOL extend_selection)
}
else
{
+ updateSort();
+
item_list::reverse_iterator iter;
for (iter = mItemList.rbegin(); iter != mItemList.rend(); iter++)
{
@@ -1086,12 +1130,12 @@ LLScrollListItem* LLScrollListCtrl::addSeparator(EAddPosition pos)
{
LLScrollListItem::Params separator_params;
separator_params.enabled(false);
- LLScrollListCell::Params cell_params;
- cell_params.type = "icon";
- cell_params.value = "menu_separator";
- cell_params.color = LLColor4(0.f, 0.f, 0.f, 0.7f);
- cell_params.font_halign = LLFontGL::HCENTER;
- separator_params.cells.add(cell_params);
+ LLScrollListCell::Params column_params;
+ column_params.type = "icon";
+ column_params.value = "menu_separator";
+ column_params.color = LLColor4(0.f, 0.f, 0.f, 0.7f);
+ column_params.font_halign = LLFontGL::HCENTER;
+ separator_params.columns.add(column_params);
return addRow( separator_params, pos );
}
@@ -1246,14 +1290,14 @@ const std::string LLScrollListCtrl::getSelectedItemLabel(S32 column) const
// "StringUUID" interface: use this when you're creating a list that contains non-unique strings each of which
// has an associated, unique UUID, and only one of which can be selected at a time.
-LLScrollListItem* LLScrollListCtrl::addStringUUIDItem(const std::string& item_text, const LLUUID& id, EAddPosition pos, BOOL enabled, S32 column_width)
+LLScrollListItem* LLScrollListCtrl::addStringUUIDItem(const std::string& item_text, const LLUUID& id, EAddPosition pos, BOOL enabled)
{
if (getItemCount() < mMaxItemCount)
{
LLScrollListItem::Params item_p;
item_p.enabled(enabled);
item_p.value(id);
- item_p.cells.add().value(item_text).width(column_width).type("text");
+ item_p.columns.add().value(item_text).type("text");
return addRow( item_p, pos );
}
@@ -1345,19 +1389,19 @@ void LLScrollListCtrl::drawItems()
S32 y = mItemListRect.mTop - mLineHeight;
// allow for partial line at bottom
- S32 num_page_lines = mPageLines + 1;
+ S32 num_page_lines = getLinesPerPage();
LLRect item_rect;
LLGLSUIDefault gls_ui;
+ F32 alpha = getDrawContext().mAlpha;
+
{
LLLocalClipRect clip(mItemListRect);
S32 cur_y = y;
- mDrewSelected = FALSE;
-
S32 line = 0;
S32 max_columns = 0;
@@ -1375,14 +1419,10 @@ void LLScrollListCtrl::drawItems()
cur_y,
mItemListRect.getWidth(),
mLineHeight );
+ item->setRect(item_rect);
//llinfos << item_rect.getWidth() << llendl;
- if (item->getSelected())
- {
- mDrewSelected = TRUE;
- }
-
max_columns = llmax(max_columns, item->getNumColumns());
LLColor4 fg_color;
@@ -1432,7 +1472,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;
}
@@ -1447,10 +1487,7 @@ void LLScrollListCtrl::draw()
LLLocalClipRect clip(getLocalRect());
// if user specifies sort, make sure it is maintained
- if (needsSorting() && !isSorted())
- {
- sortItems();
- }
+ updateSort();
if (mNeedsScroll)
{
@@ -1477,7 +1514,7 @@ void LLScrollListCtrl::draw()
if (mBorder)
{
- mBorder->setKeyboardFocusHighlight(gFocusMgr.getKeyboardFocus() == this);
+ mBorder->setKeyboardFocusHighlight(hasFocus());
}
LLUICtrl::draw();
@@ -1495,10 +1532,28 @@ BOOL LLScrollListCtrl::handleScrollWheel(S32 x, S32 y, S32 clicks)
BOOL handled = FALSE;
// Pretend the mouse is over the scrollbar
handled = mScrollbar->handleScrollWheel( 0, 0, clicks );
+
+ if (mMouseWheelOpaque)
+ {
+ return TRUE;
+ }
+
return handled;
}
-BOOL LLScrollListCtrl::handleToolTip(S32 x, S32 y, std::string& msg, LLRect* sticky_rect_screen)
+// *NOTE: Requires a valid row_index and column_index
+LLRect LLScrollListCtrl::getCellRect(S32 row_index, S32 column_index)
+{
+ LLRect cell_rect;
+ S32 rect_left = getColumnOffsetFromIndex(column_index) + mItemListRect.mLeft;
+ S32 rect_bottom = getRowOffsetFromIndex(row_index);
+ LLScrollListColumn* columnp = getColumn(column_index);
+ cell_rect.setOriginAndSize(rect_left, rect_bottom,
+ /*rect_left + */columnp->getWidth(), mLineHeight);
+ return cell_rect;
+}
+
+BOOL LLScrollListCtrl::handleToolTip(S32 x, S32 y, MASK mask)
{
S32 column_index = getColumnIndexFromOffset(x);
LLScrollListColumn* columnp = getColumn(column_index);
@@ -1512,24 +1567,23 @@ BOOL LLScrollListCtrl::handleToolTip(S32 x, S32 y, std::string& msg, LLRect* sti
{
LLScrollListCell* hit_cell = hit_item->getColumn(column_index);
if (!hit_cell) return FALSE;
- //S32 cell_required_width = hit_cell->getContentWidth();
if (hit_cell
- && hit_cell->isText())
+ && hit_cell->isText()
+ && hit_cell->needsToolTip())
{
-
- S32 rect_left = getColumnOffsetFromIndex(column_index) + mItemListRect.mLeft;
- S32 rect_bottom = getRowOffsetFromIndex(getItemIndex(hit_item));
- LLRect cell_rect;
- cell_rect.setOriginAndSize(rect_left, rect_bottom, rect_left + columnp->getWidth(), mLineHeight);
+ S32 row_index = getItemIndex(hit_item);
+ LLRect cell_rect = getCellRect(row_index, column_index);
// Convert rect local to screen coordinates
- localPointToScreen(
- cell_rect.mLeft, cell_rect.mBottom,
- &(sticky_rect_screen->mLeft), &(sticky_rect_screen->mBottom) );
- localPointToScreen(
- cell_rect.mRight, cell_rect.mTop,
- &(sticky_rect_screen->mRight), &(sticky_rect_screen->mTop) );
+ LLRect sticky_rect;
+ localRectToScreen(cell_rect, &sticky_rect);
- msg = hit_cell->getValue().asString();
+ // display tooltip exactly over original cell, in same font
+ LLToolTipMgr::instance().show(LLToolTip::Params()
+ .message(hit_cell->getToolTip())
+ .font(LLFontGL::getFontSansSerifSmall())
+ .pos(LLCoordGL(sticky_rect.mLeft - 5, sticky_rect.mTop + 6))
+ .delay_time(0.2f)
+ .sticky_rect(sticky_rect));
}
handled = TRUE;
}
@@ -1538,8 +1592,7 @@ BOOL LLScrollListCtrl::handleToolTip(S32 x, S32 y, std::string& msg, LLRect* sti
LLScrollColumnHeader* headerp = columnp->mHeader;
if (headerp && !handled)
{
- headerp->handleToolTip(x, y, msg, sticky_rect_screen);
- handled = !msg.empty();
+ handled = headerp->handleToolTip(x, y, mask);
}
return handled;
@@ -1670,7 +1723,7 @@ BOOL LLScrollListCtrl::handleMouseDown(S32 x, S32 y, MASK mask)
}
BOOL LLScrollListCtrl::handleMouseUp(S32 x, S32 y, MASK mask)
-{
+{
if (hasMouseCapture())
{
// release mouse capture immediately so
@@ -1694,6 +1747,72 @@ BOOL LLScrollListCtrl::handleMouseUp(S32 x, S32 y, MASK mask)
return LLUICtrl::handleMouseUp(x, y, mask);
}
+// virtual
+BOOL LLScrollListCtrl::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+ LLScrollListItem *item = hitItem(x, y);
+ if (item)
+ {
+ // check to see if we have a UUID for this row
+ std::string id = item->getValue().asString();
+ LLUUID uuid(id);
+ if (! uuid.isNull() && mContextMenuType != MENU_NONE)
+ {
+ // set up the callbacks for all of the avatar/group menu items
+ // (N.B. callbacks don't take const refs as id is local scope)
+ bool is_group = (mContextMenuType == MENU_GROUP);
+ LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
+ registrar.add("Url.Execute", boost::bind(&LLScrollListCtrl::showNameDetails, id, is_group));
+ registrar.add("Url.CopyLabel", boost::bind(&LLScrollListCtrl::copyNameToClipboard, id, is_group));
+ registrar.add("Url.CopyUrl", boost::bind(&LLScrollListCtrl::copySLURLToClipboard, id, is_group));
+
+ // create the context menu from the XUI file and display it
+ std::string menu_name = is_group ? "menu_url_group.xml" : "menu_url_agent.xml";
+ delete mPopupMenu;
+ mPopupMenu = LLUICtrlFactory::getInstance()->createFromFile<LLContextMenu>(
+ menu_name, LLMenuGL::sMenuContainer, LLMenuHolderGL::child_registry_t::instance());
+ if (mPopupMenu)
+ {
+ mPopupMenu->show(x, y);
+ LLMenuGL::showPopup(this, mPopupMenu, x, y);
+ return TRUE;
+ }
+ }
+ }
+ return FALSE;
+}
+
+void LLScrollListCtrl::showNameDetails(std::string id, bool is_group)
+{
+ // show the resident's profile or the group profile
+ std::string sltype = is_group ? "group" : "agent";
+ std::string slurl = "secondlife:///app/" + sltype + "/" + id + "/about";
+ LLUrlAction::clickAction(slurl);
+}
+
+void LLScrollListCtrl::copyNameToClipboard(std::string id, bool is_group)
+{
+ // copy the name of the avatar or group to the clipboard
+ std::string name;
+ if (is_group)
+ {
+ gCacheName->getGroupName(LLUUID(id), name);
+ }
+ else
+ {
+ gCacheName->getFullName(LLUUID(id), name);
+ }
+ LLUrlAction::copyURLToClipboard(name);
+}
+
+void LLScrollListCtrl::copySLURLToClipboard(std::string id, bool is_group)
+{
+ // copy a SLURL for the avatar or group to the clipboard
+ std::string sltype = is_group ? "group" : "agent";
+ std::string slurl = "secondlife:///app/" + sltype + "/" + id + "/about";
+ LLUrlAction::copyURLToClipboard(slurl);
+}
+
BOOL LLScrollListCtrl::handleDoubleClick(S32 x, S32 y, MASK mask)
{
//BOOL handled = FALSE;
@@ -1705,7 +1824,8 @@ BOOL LLScrollListCtrl::handleDoubleClick(S32 x, S32 y, MASK mask)
// so the scroll bars will work.
if (NULL == LLView::childrenHandleDoubleClick(x, y, mask))
{
- if( mCanSelect && mOnDoubleClickCallback )
+ // Run the callback only if an item is being double-clicked.
+ if( mCanSelect && hitItem(x, y) && mOnDoubleClickCallback )
{
mOnDoubleClickCallback();
}
@@ -1774,6 +1894,8 @@ LLScrollListItem* LLScrollListCtrl::hitItem( S32 x, S32 y )
// Excludes disabled items.
LLScrollListItem* hit_item = NULL;
+ updateSort();
+
LLRect item_rect;
item_rect.setLeftTopAndSize(
mItemListRect.mLeft,
@@ -1782,7 +1904,7 @@ LLScrollListItem* LLScrollListCtrl::hitItem( S32 x, S32 y )
mLineHeight );
// allow for partial line at bottom
- S32 num_page_lines = mPageLines + 1;
+ S32 num_page_lines = getLinesPerPage();
S32 line = 0;
item_list::iterator iter;
@@ -1853,8 +1975,7 @@ S32 LLScrollListCtrl::getColumnOffsetFromIndex(S32 index)
S32 LLScrollListCtrl::getRowOffsetFromIndex(S32 index)
{
- S32 row_bottom = ((mItemListRect.mTop - (index - mScrollLines)) * mLineHeight)
- - mLineHeight;
+ S32 row_bottom = (mItemListRect.mTop - ((index - mScrollLines + 1) * mLineHeight) );
return row_bottom;
}
@@ -2219,6 +2340,8 @@ BOOL LLScrollListCtrl::setSort(S32 column_idx, BOOL ascending)
sort_column->mSortDirection = ascending ? LLScrollListColumn::ASCENDING : LLScrollListColumn::DESCENDING;
sort_column_t new_sort_column(column_idx, ascending);
+
+ setNeedsSort();
if (mSortColumns.empty())
{
@@ -2240,6 +2363,20 @@ BOOL LLScrollListCtrl::setSort(S32 column_idx, BOOL ascending)
}
}
+S32 LLScrollListCtrl::getLinesPerPage()
+{
+ //if mPageLines is NOT provided display all item
+ if(mPageLines)
+ {
+ return mPageLines;
+ }
+ else
+ {
+ return mLineHeight ? mItemListRect.getHeight() / mLineHeight : getItemCount();
+ }
+}
+
+
// Called by scrollbar
void LLScrollListCtrl::onScrollChange( S32 new_pos, LLScrollbar* scrollbar )
{
@@ -2259,21 +2396,22 @@ void LLScrollListCtrl::sortByColumn(const std::string& name, BOOL ascending)
// First column is column 0
void LLScrollListCtrl::sortByColumnIndex(U32 column, BOOL ascending)
{
- if (setSort(column, ascending))
- {
- sortItems();
- }
+ setSort(column, ascending);
+ updateSort();
}
-void LLScrollListCtrl::sortItems()
+void LLScrollListCtrl::updateSort() const
{
- // do stable sort to preserve any previous sorts
- std::stable_sort(
- mItemList.begin(),
- mItemList.end(),
- SortScrollListItem(mSortColumns));
+ if (hasSortOrder() && !isSorted())
+ {
+ // do stable sort to preserve any previous sorts
+ std::stable_sort(
+ mItemList.begin(),
+ mItemList.end(),
+ SortScrollListItem(mSortColumns,mSortCallback));
- setSorted(TRUE);
+ mSorted = true;
+ }
}
// for one-shot sorts, does not save sort column/order
@@ -2286,7 +2424,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()
@@ -2329,10 +2467,7 @@ void LLScrollListCtrl::scrollToShowSelected()
return;
}
- if (needsSorting() && !isSorted())
- {
- sortItems();
- }
+ updateSort();
S32 index = getFirstSelectedIndex();
if (index < 0)
@@ -2348,7 +2483,8 @@ void LLScrollListCtrl::scrollToShowSelected()
}
S32 lowest = mScrollLines;
- S32 highest = mScrollLines + mPageLines;
+ S32 page_lines = getLinesPerPage();
+ S32 highest = mScrollLines + page_lines;
if (index < lowest)
{
@@ -2357,7 +2493,7 @@ void LLScrollListCtrl::scrollToShowSelected()
}
else if (highest <= index)
{
- setScrollPos(index - mPageLines + 1);
+ setScrollPos(index - page_lines + 1);
}
}
@@ -2502,7 +2638,7 @@ void LLScrollListCtrl::addColumn(const LLScrollListColumn::Params& column_params
LLRect temp_rect = LLRect(left,top+mHeadingHeight,right,top);
- LLScrollColumnHeader::Params params;
+ LLScrollColumnHeader::Params params(LLUICtrlFactory::getDefaultParams<LLScrollColumnHeader>());
params.name = "btn_" + name;
params.rect = temp_rect;
params.column = new_column;
@@ -2573,7 +2709,7 @@ std::string LLScrollListCtrl::getSortColumnName()
else return "";
}
-BOOL LLScrollListCtrl::needsSorting()
+BOOL LLScrollListCtrl::hasSortOrder() const
{
return !mSortColumns.empty();
}
@@ -2639,16 +2775,20 @@ 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
S32 col_index = 0;
- for(LLInitParam::ParamIterator<LLScrollListCell::Params>::const_iterator itor = item_p.cells().begin();
- itor != item_p.cells().end();
+ for(LLInitParam::ParamIterator<LLScrollListCell::Params>::const_iterator itor = item_p.columns().begin();
+ itor != item_p.columns().end();
++itor)
{
LLScrollListCell::Params cell_p = *itor;
@@ -2699,7 +2839,7 @@ LLScrollListItem* LLScrollListCtrl::addRow(const LLScrollListItem::Params& item_
col_index++;
}
- if (item_p.cells().empty())
+ if (item_p.columns().empty())
{
if (mColumns.empty())
{
@@ -2754,7 +2894,7 @@ LLScrollListItem* LLScrollListCtrl::addSimpleElement(const std::string& value, E
LLScrollListItem::Params item_params;
item_params.value(entry_id);
- item_params.cells.add()
+ item_params.columns.add()
.value(value)
.font(LLFontGL::getFontSansSerifSmall());
@@ -2811,7 +2951,6 @@ BOOL LLScrollListCtrl::operateOnAll(EOperation op)
//virtual
void LLScrollListCtrl::setFocus(BOOL b)
{
- mSearchString.clear();
// for tabbing into pristine scroll lists (Finder)
if (!getFirstSelected())
{
@@ -2856,6 +2995,9 @@ void LLScrollListCtrl::onFocusLost()
{
gFocusMgr.setMouseCapture(NULL);
}
+
+ mSearchString.clear();
+
LLUICtrl::onFocusLost();
}
diff --git a/indra/llui/llscrolllistctrl.h b/indra/llui/llscrolllistctrl.h
index 461df6760f..8a2f893ba2 100644
--- a/indra/llui/llscrolllistctrl.h
+++ b/indra/llui/llscrolllistctrl.h
@@ -4,31 +4,25 @@
* in UI code. LLScrollListCell, LLScrollListItem, etc. are utility
* classes.
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -44,7 +38,6 @@
#include "llfontgl.h"
#include "llui.h"
#include "llstring.h" // LLWString
-//#include "llimagegl.h"
#include "lleditmenuhandler.h"
#include "llframetimer.h"
@@ -52,9 +45,11 @@
#include "lldate.h"
#include "llscrolllistitem.h"
#include "llscrolllistcolumn.h"
+#include "llviewborder.h"
class LLScrollListCell;
class LLTextBox;
+class LLContextMenu;
class LLScrollListCtrl : public LLUICtrl, public LLEditMenuHandler,
public LLCtrlListInterface, public LLCtrlScrollInterface
@@ -72,21 +67,48 @@ 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>
{
// behavioral flags
Optional<bool> multi_select,
- commit_on_keyboard_movement;
+ commit_on_keyboard_movement,
+ mouse_wheel_opaque;
// display flags
Optional<bool> has_border,
draw_heading,
draw_stripes,
- background_visible;
+ background_visible,
+ scroll_bar_bg_visible;
// layout
Optional<S32> column_padding,
+ page_lines,
heading_height;
// sort and search behavior
@@ -100,12 +122,15 @@ public:
bg_selected_color,
fg_disable_color,
bg_writeable_color,
- bg_read_only_color,
+ bg_readonly_color,
bg_stripe_color,
hovered_color,
- highlighted_color;
+ highlighted_color,
+ scroll_bar_bg_color;
Optional<Contents> contents;
+
+ Optional<LLViewBorder::Params> border;
Params();
};
@@ -142,6 +167,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 ]
@@ -188,7 +214,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; }
@@ -222,7 +251,7 @@ public:
// DEPRECATED: Use LLSD versions of setCommentText() and getSelectedValue().
// "StringUUID" interface: use this when you're creating a list that contains non-unique strings each of which
// has an associated, unique UUID, and only one of which can be selected at a time.
- LLScrollListItem* addStringUUIDItem(const std::string& item_text, const LLUUID& id, EAddPosition pos = ADD_BOTTOM, BOOL enabled = TRUE, S32 column_width = 0);
+ LLScrollListItem* addStringUUIDItem(const std::string& item_text, const LLUUID& id, EAddPosition pos = ADD_BOTTOM, BOOL enabled = TRUE);
LLUUID getStringUUIDSelectedItem() const;
LLScrollListItem* getFirstSelected() const;
@@ -271,16 +300,21 @@ public:
void clearSearchString() { mSearchString.clear(); }
+ // support right-click context menus for avatar/group lists
+ enum ContextMenuType { MENU_NONE, MENU_AVATAR, MENU_GROUP };
+ void setContextMenu(const ContextMenuType &menu) { mContextMenuType = menu; }
+
// Overridden from LLView
/*virtual*/ void draw();
/*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask);
/*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask);
/*virtual*/ BOOL handleDoubleClick(S32 x, S32 y, MASK mask);
/*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask);
/*virtual*/ BOOL handleKeyHere(KEY key, MASK mask);
/*virtual*/ BOOL handleUnicodeCharHere(llwchar uni_char);
/*virtual*/ BOOL handleScrollWheel(S32 x, S32 y, S32 clicks);
- /*virtual*/ BOOL handleToolTip(S32 x, S32 y, std::string& msg, LLRect* sticky_rect);
+ /*virtual*/ BOOL handleToolTip(S32 x, S32 y, MASK mask);
/*virtual*/ void setEnabled(BOOL enabled);
/*virtual*/ void setFocus( BOOL b );
/*virtual*/ void onFocusReceived();
@@ -299,6 +333,9 @@ public:
LLRect getItemListRect() { return mItemListRect; }
+ /// Returns rect, in local coords, of a given row/column
+ LLRect getCellRect(S32 row_index, S32 column_index);
+
// Used "internally" by the scroll bar.
void onScrollChange( S32 new_pos, LLScrollbar* src );
@@ -309,6 +346,11 @@ public:
S32 getMaxContentWidth() { return mMaxContentWidth; }
void setHeadingHeight(S32 heading_height);
+ /**
+ * Sets max visible lines without scroolbar, if this value equals to 0,
+ * then display all items.
+ */
+ void setPageLines(S32 page_lines );
void setCollapseEmptyColumns(BOOL collapse);
LLScrollListItem* hitItem(S32 x,S32 y);
@@ -330,17 +372,25 @@ public:
std::string getSortColumnName();
BOOL getSortAscending() { return mSortColumns.empty() ? TRUE : mSortColumns.back().second; }
- BOOL needsSorting();
+ BOOL hasSortOrder() const;
- S32 selectMultiple( std::vector<LLUUID> ids );
- void sortItems();
+ S32 selectMultiple( uuid_vec_t ids );
+ // conceptually const, but mutates mItemList
+ void updateSort() const;
// sorts a list without affecting the permanent sort order (so further list insertions can be unsorted, for example)
void sortOnce(S32 column, BOOL ascending);
// manually call this whenever editing list items in place to flag need for resorting
- void setSorted(BOOL sorted) { mSorted = sorted; }
+ 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
@@ -362,11 +412,13 @@ protected:
typedef std::deque<LLScrollListItem *> item_list;
item_list& getItemList() { return mItemList; }
+ void updateLineHeight();
+
private:
void selectPrevItem(BOOL extend_selection);
void selectNextItem(BOOL extend_selection);
void drawItems();
- void updateLineHeight();
+
void updateLineHeightInsert(LLScrollListItem* item);
void reportInvalidInput();
BOOL isRepeatedChars(const LLWString& string) const;
@@ -374,9 +426,11 @@ private:
void deselectItem(LLScrollListItem* itemp);
void commitIfChanged();
BOOL setSort(S32 column, BOOL ascending);
+ S32 getLinesPerPage();
- S32 mCurIndex; // For get[First/Next]Data
- S32 mCurSelectedIndex; // For get[First/Next]Selected
+ static void showNameDetails(std::string id, bool is_group);
+ static void copyNameToClipboard(std::string id, bool is_group);
+ static void copySLURLToClipboard(std::string id, bool is_group);
S32 mLineHeight; // the max height of a single line
S32 mScrollLines; // how many lines we've scrolled down
@@ -390,11 +444,12 @@ private:
BOOL mCommitOnSelectionChange;
BOOL mSelectionChanged;
BOOL mNeedsScroll;
+ BOOL mMouseWheelOpaque;
BOOL mCanSelect;
const BOOL mDisplayColumnHeaders;
BOOL mColumnsDirty;
- item_list mItemList;
+ mutable item_list mItemList;
LLScrollListItem *mLastSelected;
@@ -424,6 +479,7 @@ private:
S32 mHighlightedItem;
class LLViewBorder* mBorder;
+ LLContextMenu *mPopupMenu;
LLWString mSearchString;
LLFrameTimer mSearchTimer;
@@ -433,7 +489,7 @@ private:
S32 mTotalStaticColumnWidth;
S32 mTotalColumnPadding;
- BOOL mSorted;
+ mutable bool mSorted;
typedef std::map<std::string, LLScrollListColumn> column_map_t;
column_map_t mColumns;
@@ -441,23 +497,15 @@ private:
BOOL mDirty;
S32 mOriginalSelection;
+ ContextMenuType mContextMenuType;
+
typedef std::vector<LLScrollListColumn*> ordered_columns_t;
ordered_columns_t mColumnsIndexed;
typedef std::pair<S32, BOOL> sort_column_t;
std::vector<sort_column_t> mSortColumns;
- // HACK: Did we draw one selected item this frame?
- BOOL mDrewSelected;
-
- LLTextBox* mCommentTextBox;
+ sort_signal_t* mSortCallback;
}; // end class LLScrollListCtrl
-#ifdef LL_WINDOWS
-#ifndef INSTANTIATE_GETCHILD_SCROLLLIST
-#pragma warning (disable : 4231)
-extern template LLScrollListCtrl* LLView::getChild<LLScrollListCtrl>( const std::string& name, BOOL recurse, BOOL create_if_missing ) const;
-#endif
-#endif
-
#endif // LL_SCROLLLISTCTRL_H
diff --git a/indra/llui/llscrolllistitem.cpp b/indra/llui/llscrolllistitem.cpp
index 2ac6925c78..d95752e31c 100644
--- a/indra/llui/llscrolllistitem.cpp
+++ b/indra/llui/llscrolllistitem.cpp
@@ -3,31 +3,25 @@
* @brief Scroll lists are composed of rows (items), each of which
* contains columns (cells).
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -36,7 +30,6 @@
#include "llscrolllistitem.h"
#include "llrect.h"
-#include "llresmgr.h" // LLFONT_SANSSERIF_SMALL
#include "llui.h"
diff --git a/indra/llui/llscrolllistitem.h b/indra/llui/llscrolllistitem.h
index 8d87137c3a..611df729b4 100644
--- a/indra/llui/llscrolllistitem.h
+++ b/indra/llui/llscrolllistitem.h
@@ -3,41 +3,33 @@
* @brief Scroll lists are composed of rows (items), each of which
* contains columns (cells).
*
- * $LicenseInfo:firstyear=2007&license=viewergpl$
- *
- * Copyright (c) 2007-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LLSCROLLLISTITEM_H
#define LLSCROLLLISTITEM_H
-#include "llfontgl.h" // LLFontGL::HAlign
#include "llpointer.h" // LLPointer<>
#include "llsd.h"
-#include "lluistring.h"
#include "v4color.h"
#include "llinitparam.h"
#include "llscrolllistcell.h"
@@ -64,11 +56,11 @@ public:
Optional<void*> userdata;
Optional<LLSD> value;
- Deprecated name; // use for localization tools
- Deprecated type;
- Deprecated length;
+ Ignored name; // use for localization tools
+ Ignored type;
+ Ignored length;
- Multiple<LLScrollListCell::Params> cells;
+ Multiple<LLScrollListCell::Params> columns;
Params()
: enabled("enabled", true),
@@ -76,9 +68,9 @@ public:
name("name"),
type("type"),
length("length"),
- cells("columns")
+ columns("columns")
{
- addSynonym(cells, "column");
+ addSynonym(columns, "column");
addSynonym(value, "id");
}
};
@@ -97,8 +89,11 @@ 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; }
+ LLRect getRect() const { return mRectangle; }
void addColumn( const LLScrollListCell::Params& p );
@@ -124,6 +119,7 @@ private:
void* mUserdata;
LLSD mItemValue;
std::vector<LLScrollListCell *> mColumns;
+ LLRect mRectangle;
};
#endif
diff --git a/indra/llui/llsdparam.cpp b/indra/llui/llsdparam.cpp
index 1b0f3c9885..54c3060c4a 100644
--- a/indra/llui/llsdparam.cpp
+++ b/indra/llui/llsdparam.cpp
@@ -3,31 +3,25 @@
* @brief parameter block abstraction for creating complex objects and
* parsing construction parameters from xml and LLSD
*
- * $LicenseInfo:firstyear=2008&license=viewergpl$
- *
- * Copyright (c) 2008-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2008&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -90,15 +84,7 @@ void LLParamSDParser::readSD(const LLSD& sd, LLInitParam::BaseBlock& block, bool
mNameStack.clear();
setParseSilently(silent);
- // must have named elements at top level to submit for parsing
- if (sd.isMap())
- {
- readSDValues(sd, block);
- }
- else
- {
- parserWarning("Top level map required for LLSD->Block conversion");
- }
+ readSDValues(sd, block);
}
void LLParamSDParser::writeSD(LLSD& sd, const LLInitParam::BaseBlock& block)
diff --git a/indra/llui/llsdparam.h b/indra/llui/llsdparam.h
index 12f28f876f..e028fc30c2 100644
--- a/indra/llui/llsdparam.h
+++ b/indra/llui/llsdparam.h
@@ -3,31 +3,25 @@
* @brief parameter block abstraction for creating complex objects and
* parsing construction parameters from xml and LLSD
*
- * $LicenseInfo:firstyear=2008&license=viewergpl$
- *
- * Copyright (c) 2008-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2008&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llui/llsearcheditor.cpp b/indra/llui/llsearcheditor.cpp
index 3171f96fcf..ea96fc573d 100644
--- a/indra/llui/llsearcheditor.cpp
+++ b/indra/llui/llsearcheditor.cpp
@@ -1,32 +1,26 @@
/**
- * @file lllineeditor.cpp
- * @brief LLLineEditor base class
+ * @file llsearcheditor.cpp
+ * @brief LLSearchEditor implementation
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -36,89 +30,140 @@
#include "llsearcheditor.h"
-static LLDefaultWidgetRegistry::Register<LLSearchEditor> r2("search_editor");
-
LLSearchEditor::LLSearchEditor(const LLSearchEditor::Params& p)
-: LLUICtrl(p)
+: LLUICtrl(p),
+ mSearchButton(NULL),
+ mClearButton(NULL)
+{
+ 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)
+ {
+ // Set up search button.
+ LLButton::Params srch_btn_params(p.search_button);
+ srch_btn_params.name(std::string("search button"));
+ srch_btn_params.rect(srch_btn_rect) ;
+ srch_btn_params.follows.flags(FOLLOWS_LEFT|FOLLOWS_TOP);
+ srch_btn_params.tab_stop(false);
+ srch_btn_params.click_callback.function(boost::bind(&LLUICtrl::onCommit, this));
+
+ mSearchButton = LLUICtrlFactory::create<LLButton>(srch_btn_params);
+ mSearchEditor->addChild(mSearchButton);
+ }
+
+ if (p.clear_button_visible)
+ {
+ // Set up clear button.
+ LLButton::Params clr_btn_params(p.clear_button);
+ clr_btn_params.name(std::string("clear button"));
+ clr_btn_params.rect(clear_btn_rect) ;
+ clr_btn_params.follows.flags(FOLLOWS_RIGHT|FOLLOWS_TOP);
+ clr_btn_params.tab_stop(false);
+ clr_btn_params.click_callback.function(boost::bind(&LLSearchEditor::onClearButtonClick, this, _2));
+
+ mClearButton = LLUICtrlFactory::create<LLButton>(clr_btn_params);
+ mSearchEditor->addChild(mClearButton);
+ }
+}
+
+//virtual
+void LLSearchEditor::draw()
{
- LLLineEditor::Params line_editor_p(p);
- line_editor_p.name("search edit box");
- line_editor_p.rect(getLocalRect());
- line_editor_p.follows.flags(FOLLOWS_ALL);
- line_editor_p.text_pad_right(getRect().getHeight());
- line_editor_p.keystroke_callback(boost::bind(&LLSearchEditor::onSearchEdit, this, _1));
-
- mSearchEdit = LLUICtrlFactory::create<LLLineEditor>(line_editor_p);
- addChild(mSearchEdit);
-
- S32 btn_width = getRect().getHeight(); // button is square, and as tall as search editor
- LLRect clear_btn_rect(getRect().getWidth() - btn_width, getRect().getHeight(), getRect().getWidth(), 0);
- LLButton::Params button_params(p.clear_search_button);
- button_params.name(std::string("clear search"));
- button_params.rect(clear_btn_rect) ;
- button_params.follows.flags(FOLLOWS_RIGHT|FOLLOWS_TOP);
- button_params.tab_stop(false);
- button_params.click_callback.function(boost::bind(&LLSearchEditor::onClearSearch, this, _2));
-
- mClearSearchButton = LLUICtrlFactory::create<LLButton>(button_params);
- mSearchEdit->addChild(mClearSearchButton);
+ if (mClearButton)
+ mClearButton->setVisible(!mSearchEditor->getWText().empty());
+
+ LLUICtrl::draw();
}
//virtual
void LLSearchEditor::setValue(const LLSD& value )
{
- mSearchEdit->setValue(value);
+ mSearchEditor->setValue(value);
}
//virtual
LLSD LLSearchEditor::getValue() const
{
- return mSearchEdit->getValue();
+ return mSearchEditor->getValue();
}
//virtual
BOOL LLSearchEditor::setTextArg( const std::string& key, const LLStringExplicit& text )
{
- return mSearchEdit->setTextArg(key, text);
+ return mSearchEditor->setTextArg(key, text);
}
//virtual
BOOL LLSearchEditor::setLabelArg( const std::string& key, const LLStringExplicit& text )
{
- return mSearchEdit->setLabelArg(key, text);
+ return mSearchEditor->setLabelArg(key, text);
}
//virtual
-void LLSearchEditor::clear()
+void LLSearchEditor::setLabel( const LLStringExplicit &new_label )
{
- if (mSearchEdit)
- {
- mSearchEdit->clear();
- }
+ mSearchEditor->setLabel(new_label);
}
-void LLSearchEditor::draw()
+//virtual
+void LLSearchEditor::clear()
{
- mClearSearchButton->setVisible(!mSearchEdit->getWText().empty());
-
- LLUICtrl::draw();
+ if (mSearchEditor)
+ {
+ mSearchEditor->clear();
+ }
}
-
-void LLSearchEditor::onSearchEdit(LLLineEditor* caller )
+//virtual
+void LLSearchEditor::setFocus( BOOL b )
{
- if (mSearchCallback)
+ if (mSearchEditor)
{
- mSearchCallback(caller->getText());
+ mSearchEditor->setFocus(b);
}
}
-void LLSearchEditor::onClearSearch(const LLSD& data)
+void LLSearchEditor::onClearButtonClick(const LLSD& data)
{
setText(LLStringUtil::null);
- if (mSearchCallback)
+ mSearchEditor->onCommit(); // force keystroke callback
+}
+
+void LLSearchEditor::handleKeystroke()
+{
+ if (mKeystrokeCallback)
{
- mSearchCallback(LLStringUtil::null);
+ mKeystrokeCallback(this, getValue());
}
}
-
diff --git a/indra/llui/llsearcheditor.h b/indra/llui/llsearcheditor.h
index d8c5093fbf..f5c3b532c4 100644
--- a/indra/llui/llsearcheditor.h
+++ b/indra/llui/llsearcheditor.h
@@ -11,88 +11,88 @@
* Pre-validation (limit which keys can be used)
* Optional line history so previous entries can be recalled by CTRL UP/DOWN
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
-#ifndef LL_LLSEARCHEDITOR_H
-#define LL_LLSEARCHEDITOR_H
+#ifndef LL_SEARCHEDITOR_H
+#define LL_SEARCHEDITOR_H
#include "lllineeditor.h"
#include "llbutton.h"
-#include <boost/function.hpp>
-
-/*
- * @brief A line editor with a button to clear it and a callback to call on every edit event.
- */
class LLSearchEditor : public LLUICtrl
{
public:
struct Params : public LLInitParam::Block<Params, LLLineEditor::Params>
{
- Optional<boost::function<void(const std::string&, void*)> > search_callback;
-
- Optional<LLButton::Params> clear_search_button;
+ Optional<LLButton::Params> search_button,
+ clear_button;
+ Optional<bool> search_button_visible,
+ clear_button_visible;
+ Optional<commit_callback_t> keystroke_callback;
Params()
- : clear_search_button("clear_search_button")
+ : search_button("search_button"),
+ search_button_visible("search_button_visible"),
+ clear_button("clear_button"),
+ clear_button_visible("clear_button_visible")
{
name = "search_editor";
}
};
+ void setCommitOnFocusLost(BOOL b) { if (mSearchEditor) mSearchEditor->setCommitOnFocusLost(b); }
+
protected:
LLSearchEditor(const Params&);
friend class LLUICtrlFactory;
+
public:
virtual ~LLSearchEditor() {}
/*virtual*/ void draw();
- void setText(const LLStringExplicit &new_text) { mSearchEdit->setText(new_text); }
-
- typedef boost::function<void (const std::string& search_string)> search_callback_t;
- void setSearchCallback(search_callback_t cb) { mSearchCallback = cb; }
+ void setText(const LLStringExplicit &new_text) { mSearchEditor->setText(new_text); }
+ const std::string& getText() const { return mSearchEditor->getText(); }
// LLUICtrl interface
virtual void setValue(const LLSD& value );
virtual LLSD getValue() const;
virtual BOOL setTextArg( const std::string& key, const LLStringExplicit& text );
virtual BOOL setLabelArg( const std::string& key, const LLStringExplicit& text );
+ virtual void setLabel( const LLStringExplicit &new_label );
virtual void clear();
+ virtual void setFocus( BOOL b );
-private:
- void onSearchEdit(LLLineEditor* caller );
- void onClearSearch(const LLSD& data);
+ void setKeystrokeCallback( commit_callback_t cb ) { mKeystrokeCallback = cb; }
+
+protected:
+ void onClearButtonClick(const LLSD& data);
+ virtual void handleKeystroke();
- LLLineEditor* mSearchEdit;
- LLButton* mClearSearchButton;
- search_callback_t mSearchCallback;
+ commit_callback_t mKeystrokeCallback;
+ LLLineEditor* mSearchEditor;
+ LLButton* mSearchButton;
+ LLButton* mClearButton;
};
-#endif // LL_LLSEARCHEDITOR_H
+#endif // LL_SEARCHEDITOR_H
diff --git a/indra/llui/llslider.cpp b/indra/llui/llslider.cpp
index 66ed0d4e88..013950a5ad 100644
--- a/indra/llui/llslider.cpp
+++ b/indra/llui/llslider.cpp
@@ -2,31 +2,25 @@
* @file llslider.cpp
* @brief LLSlider base class
*
- * $LicenseInfo:firstyear=2002&license=viewergpl$
- *
- * Copyright (c) 2002-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -40,18 +34,24 @@
#include "llfocusmgr.h"
#include "llkeyboard.h" // for the MASK constants
#include "llcontrol.h"
-#include "llimagegl.h"
#include "lluictrlfactory.h"
-static LLDefaultWidgetRegistry::Register<LLSlider> r1("slider_bar");
+static LLDefaultChildRegistry::Register<LLSlider> r1("slider_bar");
+//FIXME: make this into an unregistered template so that code constructed sliders don't
+// have ambigious template lookup problem
LLSlider::Params::Params()
-: track_color("track_color"),
+: orientation ("orientation", std::string ("horizontal")),
+ track_color("track_color"),
thumb_outline_color("thumb_outline_color"),
thumb_center_color("thumb_center_color"),
thumb_image("thumb_image"),
- track_image("track_image"),
- track_highlight_image("track_highlight_image"),
+ thumb_image_pressed("thumb_image_pressed"),
+ thumb_image_disabled("thumb_image_disabled"),
+ track_image_horizontal("track_image_horizontal"),
+ track_image_vertical("track_image_vertical"),
+ track_highlight_horizontal_image("track_highlight_horizontal_image"),
+ track_highlight_vertical_image("track_highlight_vertical_image"),
mouse_down_callback("mouse_down_callback"),
mouse_up_callback("mouse_up_callback")
{
@@ -61,12 +61,19 @@ LLSlider::Params::Params()
LLSlider::LLSlider(const LLSlider::Params& p)
: LLF32UICtrl(p),
mMouseOffset( 0 ),
+ mOrientation ((p.orientation() == "horizontal") ? HORIZONTAL : VERTICAL),
mTrackColor(p.track_color()),
mThumbOutlineColor(p.thumb_outline_color()),
mThumbCenterColor(p.thumb_center_color()),
mThumbImage(p.thumb_image),
- mTrackImage(p.track_image),
- mTrackHighlightImage(p.track_highlight_image)
+ mThumbImagePressed(p.thumb_image_pressed),
+ mThumbImageDisabled(p.thumb_image_disabled),
+ mTrackImageHorizontal(p.track_image_horizontal),
+ mTrackImageVertical(p.track_image_vertical),
+ mTrackHighlightHorizontalImage(p.track_highlight_horizontal_image),
+ mTrackHighlightVerticalImage(p.track_highlight_vertical_image),
+ mMouseDownSignal(NULL),
+ mMouseUpSignal(NULL)
{
mViewModel->setValue(p.initial_value);
updateThumbRect();
@@ -75,9 +82,19 @@ LLSlider::LLSlider(const LLSlider::Params& p)
setValue(getValueF32());
if (p.mouse_down_callback.isProvided())
- initCommitCallback(p.mouse_down_callback, mMouseDownSignal);
+ {
+ setMouseDownCallback(initCommitCallback(p.mouse_down_callback));
+ }
if (p.mouse_up_callback.isProvided())
- initCommitCallback(p.mouse_up_callback, mMouseUpSignal);
+ {
+ setMouseUpCallback(initCommitCallback(p.mouse_up_callback));
+ }
+}
+
+LLSlider::~LLSlider()
+{
+ delete mMouseDownSignal;
+ delete mMouseUpSignal;
}
void LLSlider::setValue(F32 value, BOOL from_event)
@@ -106,14 +123,29 @@ void LLSlider::updateThumbRect()
S32 thumb_width = mThumbImage ? mThumbImage->getWidth() : DEFAULT_THUMB_SIZE;
S32 thumb_height = mThumbImage ? mThumbImage->getHeight() : DEFAULT_THUMB_SIZE;
- S32 left_edge = (thumb_width / 2);
- S32 right_edge = getRect().getWidth() - (thumb_width / 2);
-
- S32 x = left_edge + S32( t * (right_edge - left_edge) );
- mThumbRect.mLeft = x - (thumb_width / 2);
- mThumbRect.mRight = mThumbRect.mLeft + thumb_width;
- mThumbRect.mBottom = getLocalRect().getCenterY() - (thumb_height / 2);
- mThumbRect.mTop = mThumbRect.mBottom + thumb_height;
+
+ if ( mOrientation == HORIZONTAL )
+ {
+ S32 left_edge = (thumb_width / 2);
+ S32 right_edge = getRect().getWidth() - (thumb_width / 2);
+
+ S32 x = left_edge + S32( t * (right_edge - left_edge) );
+ mThumbRect.mLeft = x - (thumb_width / 2);
+ mThumbRect.mRight = mThumbRect.mLeft + thumb_width;
+ mThumbRect.mBottom = getLocalRect().getCenterY() - (thumb_height / 2);
+ mThumbRect.mTop = mThumbRect.mBottom + thumb_height;
+ }
+ else
+ {
+ S32 top_edge = (thumb_height / 2);
+ S32 bottom_edge = getRect().getHeight() - (thumb_height / 2);
+
+ S32 y = top_edge + S32( t * (bottom_edge - top_edge) );
+ mThumbRect.mLeft = getLocalRect().getCenterX() - (thumb_width / 2);
+ mThumbRect.mRight = mThumbRect.mLeft + thumb_width;
+ mThumbRect.mBottom = y - (thumb_height / 2);
+ mThumbRect.mTop = mThumbRect.mBottom + thumb_height;
+ }
}
@@ -133,18 +165,32 @@ BOOL LLSlider::handleHover(S32 x, S32 y, MASK mask)
{
if( hasMouseCapture() )
{
- S32 thumb_half_width = mThumbImage->getWidth()/2;
- S32 left_edge = thumb_half_width;
- S32 right_edge = getRect().getWidth() - (thumb_half_width);
+ if ( mOrientation == HORIZONTAL )
+ {
+ S32 thumb_half_width = mThumbImage->getWidth()/2;
+ S32 left_edge = thumb_half_width;
+ S32 right_edge = getRect().getWidth() - (thumb_half_width);
+
+ x += mMouseOffset;
+ x = llclamp( x, left_edge, right_edge );
- x += mMouseOffset;
- x = llclamp( x, left_edge, right_edge );
+ F32 t = F32(x - left_edge) / (right_edge - left_edge);
+ setValueAndCommit(t * (mMaxValue - mMinValue) + mMinValue );
+ }
+ else // mOrientation == VERTICAL
+ {
+ S32 thumb_half_height = mThumbImage->getHeight()/2;
+ S32 top_edge = thumb_half_height;
+ S32 bottom_edge = getRect().getHeight() - (thumb_half_height);
- F32 t = F32(x - left_edge) / (right_edge - left_edge);
- setValueAndCommit(t * (mMaxValue - mMinValue) + mMinValue );
+ y += mMouseOffset;
+ y = llclamp(y, top_edge, bottom_edge);
+ F32 t = F32(y - top_edge) / (bottom_edge - top_edge);
+ setValueAndCommit(t * (mMaxValue - mMinValue) + mMinValue );
+ }
getWindow()->setCursor(UI_CURSOR_ARROW);
- lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (active)" << llendl;
+ lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (active)" << llendl;
}
else
{
@@ -162,7 +208,8 @@ BOOL LLSlider::handleMouseUp(S32 x, S32 y, MASK mask)
{
gFocusMgr.setMouseCapture( NULL );
- mMouseUpSignal( this, getValueF32() );
+ if (mMouseUpSignal)
+ (*mMouseUpSignal)( this, getValueF32() );
handled = TRUE;
make_ui_sound("UISndClickRelease");
@@ -182,7 +229,8 @@ BOOL LLSlider::handleMouseDown(S32 x, S32 y, MASK mask)
{
setFocus(TRUE);
}
- mMouseDownSignal( this, getValueF32() );
+ if (mMouseDownSignal)
+ (*mMouseDownSignal)( this, getValueF32() );
if (MASK_CONTROL & mask) // if CTRL is modifying
{
@@ -193,7 +241,9 @@ BOOL LLSlider::handleMouseDown(S32 x, S32 y, MASK mask)
// Find the offset of the actual mouse location from the center of the thumb.
if (mThumbRect.pointInRect(x,y))
{
- mMouseOffset = (mThumbRect.mLeft + mThumbImage->getWidth()/2) - x;
+ mMouseOffset = (mOrientation == HORIZONTAL)
+ ? (mThumbRect.mLeft + mThumbImage->getWidth()/2) - x
+ : (mThumbRect.mBottom + mThumbImage->getHeight()/2) - y;
}
else
{
@@ -215,15 +265,12 @@ BOOL LLSlider::handleKeyHere(KEY key, MASK mask)
BOOL handled = FALSE;
switch(key)
{
- case KEY_UP:
case KEY_DOWN:
- // eat up and down keys to be consistent
- handled = TRUE;
- break;
case KEY_LEFT:
setValueAndCommit(getValueF32() - getIncrement());
handled = TRUE;
break;
+ case KEY_UP:
case KEY_RIGHT:
setValueAndCommit(getValueF32() + getIncrement());
handled = TRUE;
@@ -234,8 +281,21 @@ BOOL LLSlider::handleKeyHere(KEY key, MASK mask)
return handled;
}
+BOOL LLSlider::handleScrollWheel(S32 x, S32 y, S32 clicks)
+{
+ if ( mOrientation == VERTICAL )
+ {
+ F32 new_val = getValueF32() - clicks * getIncrement();
+ setValueAndCommit(new_val);
+ return TRUE;
+ }
+ return LLF32UICtrl::handleScrollWheel(x,y,clicks);
+}
+
void LLSlider::draw()
{
+ F32 alpha = getDrawContext().mAlpha;
+
// since thumb image might still be decoding, need thumb to accomodate image size
updateThumbRect();
@@ -244,32 +304,83 @@ void LLSlider::draw()
// drawing solids requires texturing be disabled
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
- F32 opacity = getEnabled() ? 1.f : 0.3f;
- LLColor4 center_color = (mThumbCenterColor.get() % opacity);
- LLColor4 track_color = (mTrackColor.get() % opacity);
-
// Track
- LLRect track_rect(mThumbImage->getWidth() / 2,
- getLocalRect().getCenterY() + (mTrackImage->getHeight() / 2),
- getRect().getWidth() - mThumbImage->getWidth() / 2,
- getLocalRect().getCenterY() - (mTrackImage->getHeight() / 2) );
- LLRect highlight_rect(track_rect.mLeft, track_rect.mTop, mThumbRect.getCenterX(), track_rect.mBottom);
- mTrackImage->draw(track_rect);
- mTrackHighlightImage->draw(highlight_rect);
+ LLPointer<LLUIImage>& trackImage = ( mOrientation == HORIZONTAL )
+ ? mTrackImageHorizontal
+ : mTrackImageVertical;
- // Thumb
- if( hasMouseCapture() )
+ LLPointer<LLUIImage>& trackHighlightImage = ( mOrientation == HORIZONTAL )
+ ? mTrackHighlightHorizontalImage
+ : mTrackHighlightVerticalImage;
+
+ LLRect track_rect;
+ LLRect highlight_rect;
+
+ if ( mOrientation == HORIZONTAL )
{
- // Show ghost where thumb was before dragging began.
- mThumbImage->draw(mDragStartThumbRect, mThumbCenterColor.get() % 0.3f);
+ track_rect.set(mThumbImage->getWidth() / 2,
+ getLocalRect().getCenterY() + (trackImage->getHeight() / 2),
+ getRect().getWidth() - mThumbImage->getWidth() / 2,
+ getLocalRect().getCenterY() - (trackImage->getHeight() / 2) );
+ highlight_rect.set(track_rect.mLeft, track_rect.mTop, mThumbRect.getCenterX(), track_rect.mBottom);
+ }
+ else
+ {
+ track_rect.set(getLocalRect().getCenterX() - (trackImage->getWidth() / 2),
+ getRect().getHeight(),
+ getLocalRect().getCenterX() + (trackImage->getWidth() / 2),
+ 0);
+ highlight_rect.set(track_rect.mLeft, track_rect.mTop, track_rect.mRight, track_rect.mBottom);
}
+
+ trackImage->draw(track_rect, LLColor4::white % alpha);
+ trackHighlightImage->draw(highlight_rect, LLColor4::white % alpha);
+
+ // Thumb
if (hasFocus())
{
// Draw focus highlighting.
- mThumbImage->drawBorder(mThumbRect, gFocusMgr.getFocusColor(), gFocusMgr.getFocusFlashWidth());
+ mThumbImage->drawBorder(mThumbRect, gFocusMgr.getFocusColor() % alpha, gFocusMgr.getFocusFlashWidth());
}
- // Fill in the thumb.
- mThumbImage->draw(mThumbRect, hasMouseCapture() ? mThumbOutlineColor.get() : center_color);
+ if( hasMouseCapture() ) // currently clicking on slider
+ {
+ // Show ghost where thumb was before dragging began.
+ if (mThumbImage.notNull())
+ {
+ mThumbImage->draw(mDragStartThumbRect, mThumbCenterColor.get() % (0.3f * alpha));
+ }
+ if (mThumbImagePressed.notNull())
+ {
+ mThumbImagePressed->draw(mThumbRect, mThumbOutlineColor % alpha);
+ }
+ }
+ else if (!isInEnabledChain())
+ {
+ if (mThumbImageDisabled.notNull())
+ {
+ mThumbImageDisabled->draw(mThumbRect, mThumbCenterColor % alpha);
+ }
+ }
+ else
+ {
+ if (mThumbImage.notNull())
+ {
+ mThumbImage->draw(mThumbRect, mThumbCenterColor % alpha);
+ }
+ }
+
LLUICtrl::draw();
}
+
+boost::signals2::connection LLSlider::setMouseDownCallback( const commit_signal_t::slot_type& cb )
+{
+ if (!mMouseDownSignal) mMouseDownSignal = new commit_signal_t();
+ return mMouseDownSignal->connect(cb);
+}
+
+boost::signals2::connection LLSlider::setMouseUpCallback( const commit_signal_t::slot_type& cb )
+{
+ if (!mMouseUpSignal) mMouseUpSignal = new commit_signal_t();
+ return mMouseUpSignal->connect(cb);
+}
diff --git a/indra/llui/llslider.h b/indra/llui/llslider.h
index 39c55afd8c..68823ed68e 100644
--- a/indra/llui/llslider.h
+++ b/indra/llui/llslider.h
@@ -2,31 +2,25 @@
* @file llslider.h
* @brief A simple slider with no label.
*
- * $LicenseInfo:firstyear=2002&license=viewergpl$
- *
- * Copyright (c) 2002-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -36,20 +30,26 @@
#include "llf32uictrl.h"
#include "v4color.h"
-class LLImageGL;
-
class LLSlider : public LLF32UICtrl
{
public:
+ enum ORIENTATION { HORIZONTAL, VERTICAL };
+
struct Params : public LLInitParam::Block<Params, LLF32UICtrl::Params>
{
+ Optional<std::string> orientation;
+
Optional<LLUIColor> track_color,
thumb_outline_color,
thumb_center_color;
Optional<LLUIImage*> thumb_image,
- track_image,
- track_highlight_image;
+ thumb_image_pressed,
+ thumb_image_disabled,
+ track_image_horizontal,
+ track_image_vertical,
+ track_highlight_horizontal_image,
+ track_highlight_vertical_image;
Optional<CommitCallbackParam> mouse_down_callback,
mouse_up_callback;
@@ -61,19 +61,24 @@ protected:
LLSlider(const Params&);
friend class LLUICtrlFactory;
public:
+ virtual ~LLSlider();
void setValue( F32 value, BOOL from_event = FALSE );
// overrides for LLF32UICtrl methods
virtual void setValue(const LLSD& value ) { setValue((F32)value.asReal(), TRUE); }
+
+ virtual void setMinValue(const LLSD& min_value) { setMinValue((F32)min_value.asReal()); }
+ virtual void setMaxValue(const LLSD& max_value) { setMaxValue((F32)max_value.asReal()); }
virtual void setMinValue(F32 min_value) { LLF32UICtrl::setMinValue(min_value); updateThumbRect(); }
virtual void setMaxValue(F32 max_value) { LLF32UICtrl::setMaxValue(max_value); updateThumbRect(); }
- boost::signals::connection setMouseDownCallback( const commit_signal_t::slot_type& cb ) { return mMouseDownSignal.connect(cb); }
- boost::signals::connection setMouseUpCallback( const commit_signal_t::slot_type& cb ) { return mMouseUpSignal.connect(cb); }
+ boost::signals2::connection setMouseDownCallback( const commit_signal_t::slot_type& cb );
+ boost::signals2::connection setMouseUpCallback( const commit_signal_t::slot_type& cb );
virtual BOOL handleHover(S32 x, S32 y, MASK mask);
virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
virtual BOOL handleKeyHere(KEY key, MASK mask);
+ virtual BOOL handleScrollWheel(S32 x, S32 y, S32 clicks);
virtual void draw();
private:
@@ -84,17 +89,23 @@ private:
S32 mMouseOffset;
LLRect mDragStartThumbRect;
- LLUIImage* mThumbImage;
- LLUIImage* mTrackImage;
- LLUIImage* mTrackHighlightImage;
+ LLPointer<LLUIImage> mThumbImage;
+ LLPointer<LLUIImage> mThumbImagePressed;
+ LLPointer<LLUIImage> mThumbImageDisabled;
+ LLPointer<LLUIImage> mTrackImageHorizontal;
+ LLPointer<LLUIImage> mTrackImageVertical;
+ LLPointer<LLUIImage> mTrackHighlightHorizontalImage;
+ LLPointer<LLUIImage> mTrackHighlightVerticalImage;
+
+ const ORIENTATION mOrientation;
- LLRect mThumbRect;
+ LLRect mThumbRect;
LLUIColor mTrackColor;
LLUIColor mThumbOutlineColor;
LLUIColor mThumbCenterColor;
- commit_signal_t mMouseDownSignal;
- commit_signal_t mMouseUpSignal;
+ commit_signal_t* mMouseDownSignal;
+ commit_signal_t* mMouseUpSignal;
};
#endif // LL_LLSLIDER_H
diff --git a/indra/llui/llsliderctrl.cpp b/indra/llui/llsliderctrl.cpp
index de2c9759b7..d760178e35 100644
--- a/indra/llui/llsliderctrl.cpp
+++ b/indra/llui/llsliderctrl.cpp
@@ -2,31 +2,25 @@
* @file llsliderctrl.cpp
* @brief LLSliderCtrl base class
*
- * $LicenseInfo:firstyear=2002&license=viewergpl$
- *
- * Copyright (c) 2002-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -34,8 +28,6 @@
#include "llsliderctrl.h"
-#include "audioengine.h"
-
#include "llmath.h"
#include "llfontgl.h"
#include "llgl.h"
@@ -53,7 +45,7 @@
const U32 MAX_STRING_LENGTH = 10;
-static LLDefaultWidgetRegistry::Register<LLSliderCtrl> r("slider");
+static LLDefaultChildRegistry::Register<LLSliderCtrl> r("slider");
LLSliderCtrl::LLSliderCtrl(const LLSliderCtrl::Params& p)
: LLF32UICtrl(p),
@@ -65,7 +57,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;
@@ -85,9 +78,10 @@ LLSliderCtrl::LLSliderCtrl(const LLSliderCtrl::Params& p)
LLTextBox::Params params(p.slider_label);
params.rect.setIfNotProvided(label_rect);
params.font.setIfNotProvided(p.font);
- params.text(p.label);
+ params.initial_value(p.label());
mLabelBox = LLUICtrlFactory::create<LLTextBox> (params);
addChild(mLabelBox);
+ mLabelFont = params.font();
}
if (p.show_text && !p.text_width.isProvided())
@@ -124,7 +118,8 @@ LLSliderCtrl::LLSliderCtrl(const LLSliderCtrl::Params& p)
slider_p.min_value.setIfNotProvided(p.min_value);
slider_p.max_value.setIfNotProvided(p.max_value);
slider_p.increment.setIfNotProvided(p.increment);
-
+ slider_p.orientation.setIfNotProvided(p.orientation);
+
slider_p.commit_callback.function(&LLSliderCtrl::onSliderCommit);
slider_p.control_name(p.control_name);
slider_p.mouse_down_callback( p.mouse_down_callback );
@@ -142,10 +137,10 @@ 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( &LLSliderCtrl::onEditorGainFocus, this );
+ mEditor->setFocusReceivedCallback( boost::bind(&LLSliderCtrl::onEditorGainFocus, _1, this ));
// don't do this, as selecting the entire text is single clicking in some cases
// and double clicking in others
//mEditor->setSelectAllonFocusReceived(TRUE);
@@ -187,9 +182,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;
@@ -234,6 +229,10 @@ void LLSliderCtrl::updateText()
std::string text = llformat(format.c_str(), displayed_value);
if( mEditor )
{
+ // Setting editor text here to "" before using actual text is here because if text which
+ // is set is the same as the one which is actually typed into lineeditor, LLLineEditor::setText()
+ // will exit at it's beginning, so text for revert on escape won't be saved. (EXT-8536)
+ mEditor->setText( LLStringUtil::null );
mEditor->setText( text );
}
else
@@ -262,7 +261,7 @@ void LLSliderCtrl::onEditorCommit( LLUICtrl* ctrl, const LLSD& userdata )
if( self->mSlider->getMinValue() <= val && val <= self->mSlider->getMaxValue() )
{
self->setValue( val ); // set the value temporarily so that the callback can retrieve it.
- if( self->mValidateSignal( self, val ) )
+ if( !self->mValidateSignal || (*(self->mValidateSignal))( self, val ) )
{
success = TRUE;
}
@@ -296,7 +295,7 @@ void LLSliderCtrl::onSliderCommit( LLUICtrl* ctrl, const LLSD& userdata )
F32 new_val = self->mSlider->getValueF32();
self->mValue = new_val; // set the value temporarily so that the callback can retrieve it.
- if( self->mValidateSignal( self, new_val ) )
+ if( !self->mValidateSignal || (*(self->mValidateSignal))( self, new_val ) )
{
success = TRUE;
}
@@ -375,12 +374,12 @@ void LLSliderCtrl::setPrecision(S32 precision)
updateText();
}
-boost::signals::connection LLSliderCtrl::setSliderMouseDownCallback( const commit_signal_t::slot_type& cb )
+boost::signals2::connection LLSliderCtrl::setSliderMouseDownCallback( const commit_signal_t::slot_type& cb )
{
return mSlider->setMouseDownCallback( cb );
}
-boost::signals::connection LLSliderCtrl::setSliderMouseUpCallback( const commit_signal_t::slot_type& cb )
+boost::signals2::connection LLSliderCtrl::setSliderMouseUpCallback( const commit_signal_t::slot_type& cb )
{
return mSlider->setMouseUpCallback( cb );
}
@@ -398,4 +397,3 @@ void LLSliderCtrl::reportInvalidData()
make_ui_sound("UISndBadKeystroke");
}
-
diff --git a/indra/llui/llsliderctrl.h b/indra/llui/llsliderctrl.h
index 0bcb1ccc9b..5153e33f49 100644
--- a/indra/llui/llsliderctrl.h
+++ b/indra/llui/llsliderctrl.h
@@ -2,31 +2,25 @@
* @file llsliderctrl.h
* @brief Decorated wrapper for a LLSlider.
*
- * $LicenseInfo:firstyear=2002&license=viewergpl$
- *
- * Copyright (c) 2002-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -46,6 +40,7 @@ class LLSliderCtrl : public LLF32UICtrl
public:
struct Params : public LLInitParam::Block<Params, LLF32UICtrl::Params>
{
+ Optional<std::string> orientation;
Optional<S32> label_width;
Optional<S32> text_width;
Optional<bool> show_text;
@@ -78,7 +73,8 @@ public:
value_text("value_text"),
slider_label("slider_label"),
mouse_down_callback("mouse_down_callback"),
- mouse_up_callback("mouse_up_callback")
+ mouse_up_callback("mouse_up_callback"),
+ orientation("orientation", std::string ("horizontal"))
{}
};
protected:
@@ -100,19 +96,22 @@ public:
/*virtual*/ void setEnabled( BOOL b );
/*virtual*/ void clear();
+
+ /*virtual*/ void setMinValue(const LLSD& min_value) { setMinValue((F32)min_value.asReal()); }
+ /*virtual*/ void setMaxValue(const LLSD& max_value) { setMaxValue((F32)max_value.asReal()); }
/*virtual*/ void setMinValue(F32 min_value) { mSlider->setMinValue(min_value); updateText(); }
/*virtual*/ void setMaxValue(F32 max_value) { mSlider->setMaxValue(max_value); updateText(); }
/*virtual*/ void setIncrement(F32 increment) { mSlider->setIncrement(increment);}
- F32 getMinValue() { return mSlider->getMinValue(); }
- F32 getMaxValue() { return mSlider->getMaxValue(); }
+ F32 getMinValue() const { return mSlider->getMinValue(); }
+ F32 getMaxValue() const { return mSlider->getMaxValue(); }
void setLabel(const LLStringExplicit& label) { if (mLabelBox) mLabelBox->setText(label); }
void setLabelColor(const LLColor4& c) { mTextEnabledColor = c; }
void setDisabledLabelColor(const LLColor4& c) { mTextDisabledColor = c; }
- boost::signals::connection setSliderMouseDownCallback( const commit_signal_t::slot_type& cb );
- boost::signals::connection setSliderMouseUpCallback( const commit_signal_t::slot_type& cb );
+ boost::signals2::connection setSliderMouseDownCallback( const commit_signal_t::slot_type& cb );
+ boost::signals2::connection setSliderMouseUpCallback( const commit_signal_t::slot_type& cb );
/*virtual*/ void onTabInto();
@@ -136,6 +135,7 @@ private:
void reportInvalidData();
const LLFontGL* mFont;
+ const LLFontGL* mLabelFont;
BOOL mShowText;
BOOL mCanEditText;
@@ -153,3 +153,4 @@ private:
};
#endif // LL_LLSLIDERCTRL_H
+
diff --git a/indra/llui/llspinctrl.cpp b/indra/llui/llspinctrl.cpp
index 72329a4b32..9decfa0b25 100644
--- a/indra/llui/llspinctrl.cpp
+++ b/indra/llui/llspinctrl.cpp
@@ -2,31 +2,25 @@
* @file llspinctrl.cpp
* @brief LLSpinCtrl base class
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -45,7 +39,6 @@
#include "lltextbox.h"
#include "llkeyboard.h"
#include "llmath.h"
-#include "audioengine.h"
#include "llcontrol.h"
#include "llfocusmgr.h"
#include "llresmgr.h"
@@ -53,14 +46,16 @@
const U32 MAX_STRING_LENGTH = 32;
-static LLDefaultWidgetRegistry::Register<LLSpinCtrl> r2("spinner");
+static LLDefaultChildRegistry::Register<LLSpinCtrl> r2("spinner");
LLSpinCtrl::Params::Params()
: label_width("label_width"),
decimal_digits("decimal_digits"),
allow_text_entry("allow_text_entry", true),
text_enabled_color("text_enabled_color"),
- text_disabled_color("text_disabled_color")
+ text_disabled_color("text_disabled_color"),
+ up_button("up_button"),
+ down_button("down_button")
{}
LLSpinCtrl::LLSpinCtrl(const LLSpinCtrl::Params& p)
@@ -74,10 +69,8 @@ LLSpinCtrl::LLSpinCtrl(const LLSpinCtrl::Params& p)
static LLUICachedControl<S32> spinctrl_spacing ("UISpinctrlSpacing", 0);
static LLUICachedControl<S32> spinctrl_btn_width ("UISpinctrlBtnWidth", 0);
static LLUICachedControl<S32> spinctrl_btn_height ("UISpinctrlBtnHeight", 0);
- S32 top = getRect().getHeight();
- S32 bottom = top - 2 * spinctrl_btn_height;
- S32 centered_top = top;
- S32 centered_bottom = bottom;
+ S32 centered_top = getRect().getHeight();
+ S32 centered_bottom = getRect().getHeight() - 2 * spinctrl_btn_height;
S32 btn_left = 0;
// reserve space for spinner
S32 label_width = llclamp(p.label_width(), 0, llmax(0, getRect().getWidth() - 40));
@@ -89,7 +82,7 @@ LLSpinCtrl::LLSpinCtrl(const LLSpinCtrl::Params& p)
LLTextBox::Params params;
params.name("SpinCtrl Label");
params.rect(label_rect);
- params.text(p.label);
+ params.initial_value(p.label());
if (p.font.isProvided())
{
params.font(p.font);
@@ -103,38 +96,18 @@ LLSpinCtrl::LLSpinCtrl(const LLSpinCtrl::Params& p)
S32 btn_right = btn_left + spinctrl_btn_width;
// Spin buttons
- LLButton::Params up_button_params;
- up_button_params.name(std::string("SpinCtrl Up"));
- up_button_params.rect
- .left(btn_left)
- .top(top)
- .right(btn_right)
- .height(spinctrl_btn_height);
- up_button_params.follows.flags(FOLLOWS_LEFT|FOLLOWS_BOTTOM);
- up_button_params.image_unselected.name("spin_up_out_blue.tga");
- up_button_params.image_selected.name("spin_up_in_blue.tga");
+ LLButton::Params up_button_params(p.up_button);
+ up_button_params.rect = LLRect(btn_left, getRect().getHeight(), btn_right, getRect().getHeight() - spinctrl_btn_height);
up_button_params.click_callback.function(boost::bind(&LLSpinCtrl::onUpBtn, this, _2));
up_button_params.mouse_held_callback.function(boost::bind(&LLSpinCtrl::onUpBtn, this, _2));
- up_button_params.tab_stop(false);
mUpBtn = LLUICtrlFactory::create<LLButton>(up_button_params);
addChild(mUpBtn);
- LLRect down_rect( btn_left, top - spinctrl_btn_height, btn_right, bottom );
-
- LLButton::Params down_button_params;
- down_button_params.name(std::string("SpinCtrl Down"));
- down_button_params.rect
- .left(btn_left)
- .right(btn_right)
- .bottom(bottom)
- .height(spinctrl_btn_height);
- down_button_params.follows.flags(FOLLOWS_LEFT|FOLLOWS_BOTTOM);
- down_button_params.image_unselected.name("spin_down_out_blue.tga");
- down_button_params.image_selected.name("spin_down_in_blue.tga");
+ LLButton::Params down_button_params(p.down_button);
+ down_button_params.rect = LLRect(btn_left, getRect().getHeight() - spinctrl_btn_height, btn_right, getRect().getHeight() - 2 * spinctrl_btn_height);
down_button_params.click_callback.function(boost::bind(&LLSpinCtrl::onDownBtn, this, _2));
down_button_params.mouse_held_callback.function(boost::bind(&LLSpinCtrl::onDownBtn, this, _2));
- down_button_params.tab_stop(false);
mDownBtn = LLUICtrlFactory::create<LLButton>(down_button_params);
addChild(mDownBtn);
@@ -148,10 +121,19 @@ 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);
+
+ if( mPrecision>0 )//should accept float numbers
+ {
+ params.prevalidate_callback(&LLTextValidate::validateFloat);
+ }
+ else //should accept int numbers
+ {
+ params.prevalidate_callback(&LLTextValidate::validateInt);
+ }
+
params.follows.flags(FOLLOWS_LEFT | FOLLOWS_BOTTOM);
mEditor = LLUICtrlFactory::create<LLLineEditor> (params);
- mEditor->setFocusReceivedCallback( &LLSpinCtrl::onEditorGainFocus, this );
+ mEditor->setFocusReceivedCallback( boost::bind(&LLSpinCtrl::onEditorGainFocus, _1, this ));
//RN: this seems to be a BAD IDEA, as it makes the editor behavior different when it has focus
// than when it doesn't. Instead, if you always have to double click to select all the text,
// it's easier to understand
@@ -183,23 +165,33 @@ void LLSpinCtrl::onUpBtn( const LLSD& data )
{
if( getEnabled() )
{
- // use getValue()/setValue() to force reload from/to control
- F32 val = (F32)getValue().asReal() + mIncrement;
- val = clamp_precision(val, mPrecision);
- val = llmin( val, mMaxValue );
-
- F32 saved_val = (F32)getValue().asReal();
- setValue(val);
- if( !mValidateSignal( this, val ) )
+ std::string text = mEditor->getText();
+ if( LLLineEditor::postvalidateFloat( text ) )
{
- setValue( saved_val );
- reportInvalidData();
- updateEditor();
- return;
- }
+
+ LLLocale locale(LLLocale::USER_LOCALE);
+ F32 cur_val = (F32) atof(text.c_str());
+
+ // use getValue()/setValue() to force reload from/to control
+ F32 val = cur_val + mIncrement;
+ val = clamp_precision(val, mPrecision);
+ val = llmin( val, mMaxValue );
+ if (val < mMinValue) val = mMinValue;
+ if (val > mMaxValue) val = mMaxValue;
+
+ F32 saved_val = (F32)getValue().asReal();
+ setValue(val);
+ if( mValidateSignal && !(*mValidateSignal)( this, val ) )
+ {
+ setValue( saved_val );
+ reportInvalidData();
+ updateEditor();
+ return;
+ }
updateEditor();
onCommit();
+ }
}
}
@@ -207,22 +199,33 @@ void LLSpinCtrl::onDownBtn( const LLSD& data )
{
if( getEnabled() )
{
- F32 val = (F32)getValue().asReal() - mIncrement;
- val = clamp_precision(val, mPrecision);
- val = llmax( val, mMinValue );
-
- F32 saved_val = (F32)getValue().asReal();
- setValue(val);
- if( !mValidateSignal( this, val ) )
+ std::string text = mEditor->getText();
+ if( LLLineEditor::postvalidateFloat( text ) )
{
- setValue( saved_val );
- reportInvalidData();
+
+ LLLocale locale(LLLocale::USER_LOCALE);
+ F32 cur_val = (F32) atof(text.c_str());
+
+ F32 val = cur_val - mIncrement;
+ val = clamp_precision(val, mPrecision);
+ val = llmax( val, mMinValue );
+
+ if (val < mMinValue) val = mMinValue;
+ if (val > mMaxValue) val = mMaxValue;
+
+ F32 saved_val = (F32)getValue().asReal();
+ setValue(val);
+ if( mValidateSignal && !(*mValidateSignal)( this, val ) )
+ {
+ setValue( saved_val );
+ reportInvalidData();
+ updateEditor();
+ return;
+ }
+
updateEditor();
- return;
+ onCommit();
}
-
- updateEditor();
- onCommit();
}
}
@@ -270,13 +273,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 )
@@ -304,7 +313,7 @@ void LLSpinCtrl::onEditorCommit( const LLSD& data )
F32 saved_val = getValueF32();
setValue(val);
- if( mValidateSignal( this, val ) )
+ if( !mValidateSignal || (*mValidateSignal)( this, val ) )
{
success = TRUE;
onCommit();
@@ -339,10 +348,7 @@ void LLSpinCtrl::setEnabled(BOOL b)
{
LLView::setEnabled( b );
mEditor->setEnabled( b );
- if( mLabelBox )
- {
- mLabelBox->setColor( b ? mTextEnabledColor.get() : mTextDisabledColor.get() );
- }
+ updateLabelColor();
}
@@ -390,6 +396,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)
diff --git a/indra/llui/llspinctrl.h b/indra/llui/llspinctrl.h
index eb1a2eb8a7..8960971594 100644
--- a/indra/llui/llspinctrl.h
+++ b/indra/llui/llspinctrl.h
@@ -2,31 +2,25 @@
* @file llspinctrl.h
* @brief Typical spinner with "up" and "down" arrow buttons.
*
- * $LicenseInfo:firstyear=2002&license=viewergpl$
- *
- * Copyright (c) 2002-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -35,6 +29,7 @@
#include "stdtypes.h"
+#include "llbutton.h"
#include "llf32uictrl.h"
#include "v4color.h"
#include "llrect.h"
@@ -53,6 +48,9 @@ public:
Optional<LLUIColor> text_enabled_color;
Optional<LLUIColor> text_disabled_color;
+ Optional<LLButton::Params> up_button;
+ Optional<LLButton::Params> down_button;
+
Params();
};
protected:
@@ -77,8 +75,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();
@@ -99,6 +97,7 @@ public:
void onDownBtn(const LLSD& data);
private:
+ void updateLabelColor();
void updateEditor();
void reportInvalidData();
diff --git a/indra/llui/llstatbar.cpp b/indra/llui/llstatbar.cpp
index bd74b285a7..ec4db14790 100644
--- a/indra/llui/llstatbar.cpp
+++ b/indra/llui/llstatbar.cpp
@@ -2,31 +2,25 @@
* @file llstatbar.cpp
* @brief A little map of the world with network information
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llui/llstatbar.h b/indra/llui/llstatbar.h
index 7de782a24f..62a9db82fe 100644
--- a/indra/llui/llstatbar.h
+++ b/indra/llui/llstatbar.h
@@ -2,31 +2,25 @@
* @file llstatbar.h
* @brief A little map of the world with network information
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llui/llstatgraph.cpp b/indra/llui/llstatgraph.cpp
index 3bd2c9f9e7..e44887ebf0 100644
--- a/indra/llui/llstatgraph.cpp
+++ b/indra/llui/llstatgraph.cpp
@@ -2,31 +2,25 @@
* @file llstatgraph.cpp
* @brief Simpler compact stat graph with tooltip
*
- * $LicenseInfo:firstyear=2002&license=viewergpl$
- *
- * Copyright (c) 2002-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -107,10 +101,10 @@ void LLStatGraph::draw()
}
//gl_drop_shadow(0, getRect().getHeight(), getRect().getWidth(), 0,
- // gSavedSkinSettings.getColor("ColorDropShadow"),
+ // LLUIColorTable::instance().getColor("ColorDropShadow"),
// (S32) gSavedSettings.getF32("DropShadowFloater") );
- color = LLUI::sSettingGroups["color"]->getColor( "MenuDefaultBgColor" );
+ color = LLUIColorTable::instance().getColor( "MenuDefaultBgColor" );
gGL.color4fv(color.mV);
gl_rect_2d(0, getRect().getHeight(), getRect().getWidth(), 0, TRUE);
diff --git a/indra/llui/llstatgraph.h b/indra/llui/llstatgraph.h
index dd38050b1b..757525e232 100644
--- a/indra/llui/llstatgraph.h
+++ b/indra/llui/llstatgraph.h
@@ -2,31 +2,25 @@
* @file llstatgraph.h
* @brief Simpler compact stat graph with tooltip
*
- * $LicenseInfo:firstyear=2002&license=viewergpl$
- *
- * Copyright (c) 2002-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llui/llstatview.cpp b/indra/llui/llstatview.cpp
index 6691f16c1e..eda2d6047f 100644
--- a/indra/llui/llstatview.cpp
+++ b/indra/llui/llstatview.cpp
@@ -2,31 +2,25 @@
* @file llstatview.cpp
* @brief Container for all statistics info.
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -65,16 +59,9 @@ LLStatView::~LLStatView()
}
-// widget registrars
-struct StatViewRegistry : public LLWidgetRegistry<StatViewRegistry>
-{};
-
static StatViewRegistry::Register<LLStatBar> r1("stat_bar");
-
-
-const widget_registry_t& LLStatView::getChildRegistry() const
-{
- return StatViewRegistry::instance();
-}
+static StatViewRegistry::Register<LLStatView> r2("stat_view");
+// stat_view can be a child of panels/etc.
+static LLDefaultChildRegistry::Register<LLStatView> r3("stat_view");
diff --git a/indra/llui/llstatview.h b/indra/llui/llstatview.h
index 20aba7782b..22a9fcd672 100644
--- a/indra/llui/llstatview.h
+++ b/indra/llui/llstatview.h
@@ -2,31 +2,25 @@
* @file llstatview.h
* @brief Container for all statistics info.
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -39,6 +33,10 @@
class LLStatBar;
+// widget registrars
+struct StatViewRegistry : public LLChildRegistry<StatViewRegistry>
+{};
+
class LLStatView : public LLContainerView
{
public:
@@ -51,9 +49,11 @@ public:
follows.flags(FOLLOWS_TOP | FOLLOWS_LEFT);
}
};
- ~LLStatView();
- virtual const widget_registry_t& getChildRegistry() const;
+ // my valid children are stored in this registry
+ typedef StatViewRegistry child_registry_t;
+
+ ~LLStatView();
protected:
LLStatView(const Params&);
diff --git a/indra/llui/llstyle.cpp b/indra/llui/llstyle.cpp
index 432d54dfee..5e09cee78b 100644
--- a/indra/llui/llstyle.cpp
+++ b/indra/llui/llstyle.cpp
@@ -2,31 +2,25 @@
* @file llstyle.cpp
* @brief Text style class
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -38,119 +32,36 @@
#include "llstring.h"
#include "llui.h"
-
-LLStyle::LLStyle()
-{
- init(TRUE, LLColor4(0,0,0,1),LLStringUtil::null);
-}
-
-LLStyle::LLStyle(const LLStyle &style)
-{
- if (this != &style)
- {
- init(style.isVisible(),style.getColor(),style.getFontString());
- if (style.isLink())
- {
- setLinkHREF(style.getLinkHREF());
- }
- mItalic = style.mItalic;
- mBold = style.mBold;
- mUnderline = style.mUnderline;
- mDropShadow = style.mDropShadow;
- mImageHeight = style.mImageHeight;
- mImageWidth = style.mImageWidth;
- mImagep = style.mImagep;
- mIsEmbeddedItem = style.mIsEmbeddedItem;
- }
- else
- {
- init(TRUE, LLColor4(0,0,0,1),LLStringUtil::null);
- }
-}
-
-LLStyle::LLStyle(BOOL is_visible, const LLColor4 &color, const std::string& font_name)
-{
- init(is_visible, color, font_name);
-}
-
-void LLStyle::init(BOOL is_visible, const LLColor4 &color, const std::string& font_name)
-{
- mVisible = is_visible;
- mColor = color;
- setFontName(font_name);
- setLinkHREF(LLStringUtil::null);
- mItalic = FALSE;
- mBold = FALSE;
- mUnderline = FALSE;
- mDropShadow = FALSE;
- mImageHeight = 0;
- mImageWidth = 0;
- mIsEmbeddedItem = FALSE;
-}
-
-
-// Copy assignment
-LLStyle &LLStyle::operator=(const LLStyle &rhs)
+LLStyle::Params::Params()
+: visible("visible", true),
+ drop_shadow("drop_shadow", LLFontGL::NO_SHADOW),
+ color("color", LLColor4::black),
+ readonly_color("readonly_color", LLColor4::black),
+ selected_color("selected_color", LLColor4::black),
+ font("font", LLFontGL::getFontMonospace()),
+ image("image"),
+ link_href("href")
+{}
+
+
+LLStyle::LLStyle(const LLStyle::Params& p)
+: mVisible(p.visible),
+ mColor(p.color),
+ mReadOnlyColor(p.readonly_color),
+ mSelectedColor(p.selected_color),
+ mFont(p.font()),
+ mLink(p.link_href),
+ mDropShadow(p.drop_shadow),
+ mImagep(p.image())
+{}
+
+void LLStyle::setFont(const LLFontGL* font)
{
- if (this != &rhs)
- {
- setVisible(rhs.isVisible());
- setColor(rhs.getColor());
- this->mFontName = rhs.getFontString();
- this->mLink = rhs.getLinkHREF();
- mImagep = rhs.mImagep;
- mImageHeight = rhs.mImageHeight;
- mImageWidth = rhs.mImageWidth;
- mItalic = rhs.mItalic;
- mBold = rhs.mBold;
- mUnderline = rhs.mUnderline;
- mDropShadow = rhs.mDropShadow;
- mIsEmbeddedItem = rhs.mIsEmbeddedItem;
- }
-
- return *this;
+ mFont = font;
}
-//virtual
-const std::string& LLStyle::getFontString() const
-{
- return mFontName;
-}
-
-//virtual
-void LLStyle::setFontName(const std::string& fontname)
-{
- mFontName = fontname;
-
- std::string fontname_lc = fontname;
- LLStringUtil::toLower(fontname_lc);
-
- // cache the font pointer for speed when rendering text
- if ((fontname_lc == "sansserif") || (fontname_lc == "sans-serif"))
- {
- mFont = LLFontGL::getFontSansSerif();
- }
- else if ((fontname_lc == "serif"))
- {
- // *TODO: Do we have a real serif font?
- mFont = LLFontGL::getFontMonospace();
- }
- else if ((fontname_lc == "sansserifbig"))
- {
- mFont = LLFontGL::getFontSansSerifBig();
- }
- else if (fontname_lc == "small")
- {
- mFont = LLFontGL::getFontSansSerifSmall();
- }
- else
- {
- mFont = LLFontGL::getFontMonospace();
- }
-}
-//virtual
-LLFontGL* LLStyle::getFont() const
+const LLFontGL* LLStyle::getFont() const
{
return mFont;
}
@@ -185,9 +96,7 @@ void LLStyle::setImage(const LLUUID& src)
mImagep = LLUI::getUIImageByID(src);
}
-
-void LLStyle::setImageSize(S32 width, S32 height)
+void LLStyle::setImage(const std::string& name)
{
- mImageWidth = width;
- mImageHeight = height;
+ mImagep = LLUI::getUIImage(name);
}
diff --git a/indra/llui/llstyle.h b/indra/llui/llstyle.h
index 890abc7d67..66cd639936 100644
--- a/indra/llui/llstyle.h
+++ b/indra/llui/llstyle.h
@@ -2,31 +2,25 @@
* @file llstyle.h
* @brief Text style class
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -34,86 +28,88 @@
#define LL_LLSTYLE_H
#include "v4color.h"
-#include "llfont.h"
#include "llui.h"
+#include "llinitparam.h"
class LLFontGL;
class LLStyle : public LLRefCount
{
public:
- LLStyle();
- LLStyle(const LLStyle &style);
- LLStyle(BOOL is_visible, const LLColor4 &color, const std::string& font_name);
-
- LLStyle &operator=(const LLStyle &rhs);
+ struct Params : public LLInitParam::Block<Params>
+ {
+ Optional<bool> visible;
+ Optional<LLFontGL::ShadowType> drop_shadow;
+ Optional<LLUIColor> color,
+ readonly_color,
+ selected_color;
+ Optional<const LLFontGL*> font;
+ Optional<LLUIImage*> image;
+ Optional<std::string> link_href;
+ Params();
+ };
+ LLStyle(const Params& p = Params());
+public:
+ const LLUIColor& getColor() const { return mColor; }
+ void setColor(const LLUIColor &color) { mColor = color; }
- virtual void init (BOOL is_visible, const LLColor4 &color, const std::string& font_name);
+ const LLUIColor& getReadOnlyColor() const { return mReadOnlyColor; }
+ void setReadOnlyColor(const LLUIColor& color) { mReadOnlyColor = color; }
- virtual const LLColor4& getColor() const { return mColor; }
- virtual void setColor(const LLColor4 &color) { mColor = color; }
+ const LLUIColor& getSelectedColor() const { return mSelectedColor; }
+ void setSelectedColor(const LLUIColor& color) { mSelectedColor = color; }
- virtual BOOL isVisible() const;
- virtual void setVisible(BOOL is_visible);
+ BOOL isVisible() const;
+ void setVisible(BOOL is_visible);
- virtual const std::string& getFontString() const;
- virtual void setFontName(const std::string& fontname);
- virtual LLFontGL* getFont() const;
+ LLFontGL::ShadowType getShadowType() const { return mDropShadow; }
- virtual const std::string& getLinkHREF() const { return mLink; }
- virtual void setLinkHREF(const std::string& href);
- virtual BOOL isLink() const;
+ void setFont(const LLFontGL* font);
+ const LLFontGL* getFont() const;
- virtual LLUIImagePtr getImage() const;
- virtual void setImage(const LLUUID& src);
+ const std::string& getLinkHREF() const { return mLink; }
+ void setLinkHREF(const std::string& href);
+ BOOL isLink() const;
- virtual BOOL isImage() const { return ((mImageWidth != 0) && (mImageHeight != 0)); }
- virtual void setImageSize(S32 width, S32 height);
+ LLUIImagePtr getImage() const;
+ void setImage(const LLUUID& src);
+ void setImage(const std::string& name);
- BOOL getIsEmbeddedItem() const { return mIsEmbeddedItem; }
- void setIsEmbeddedItem( BOOL b ) { mIsEmbeddedItem = b; }
+ BOOL isImage() const { return mImagep.notNull(); }
- // inlined here to make it easier to compare to member data below. -MG
bool operator==(const LLStyle &rhs) const
{
return
- mVisible == rhs.isVisible()
- && mColor == rhs.getColor()
- && mFontName == rhs.getFontString()
- && mLink == rhs.getLinkHREF()
+ mVisible == rhs.mVisible
+ && mColor == rhs.mColor
+ && mReadOnlyColor == rhs.mReadOnlyColor
+ && mSelectedColor == rhs.mSelectedColor
+ && mFont == rhs.mFont
+ && mLink == rhs.mLink
&& mImagep == rhs.mImagep
- && mImageHeight == rhs.mImageHeight
- && mImageWidth == rhs.mImageWidth
- && mItalic == rhs.mItalic
- && mBold == rhs.mBold
- && mUnderline == rhs.mUnderline
- && mDropShadow == rhs.mDropShadow
- && mIsEmbeddedItem == rhs.mIsEmbeddedItem;
+ && mDropShadow == rhs.mDropShadow;
}
bool operator!=(const LLStyle& rhs) const { return !(*this == rhs); }
public:
- BOOL mItalic;
- BOOL mBold;
- BOOL mUnderline;
- BOOL mDropShadow;
- S32 mImageWidth;
- S32 mImageHeight;
+ LLFontGL::ShadowType mDropShadow;
protected:
- virtual ~LLStyle() { }
+ ~LLStyle() { }
private:
- BOOL mVisible;
- LLColor4 mColor;
- std::string mFontName;
- LLFontGL* mFont; // cached for performance
- std::string mLink;
- LLUIImagePtr mImagep;
- BOOL mIsEmbeddedItem;
+ BOOL mVisible;
+ LLUIColor mColor;
+ LLUIColor mReadOnlyColor;
+ LLUIColor mSelectedColor;
+ std::string mFontName;
+ const LLFontGL* mFont;
+ std::string mLink;
+ LLUIImagePtr mImagep;
};
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 3391b1275c..7f0d650403 100644
--- a/indra/llui/lltabcontainer.cpp
+++ b/indra/llui/lltabcontainer.cpp
@@ -2,38 +2,34 @@
* @file lltabcontainer.cpp
* @brief LLTabContainer class
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "linden_common.h"
+
#include "lltabcontainer.h"
+
#include "llfocusmgr.h"
-#include "llbutton.h"
+#include "lllocalcliprect.h"
#include "llrect.h"
#include "llresizehandle.h"
#include "lltextbox.h"
@@ -41,6 +37,7 @@
#include "lluictrlfactory.h"
#include "llrender.h"
#include "llfloater.h"
+#include "lltrans.h"
//----------------------------------------------------------------------------
@@ -92,6 +89,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
@@ -99,21 +185,38 @@ struct LLPlaceHolderPanel : public LLPanel
LLPlaceHolderPanel(const Params& p) : LLPanel(p)
{}
};
-static LLDefaultWidgetRegistry::Register<LLPlaceHolderPanel> r1("placeholder");
-static LLDefaultWidgetRegistry::Register<LLTabContainer> r2("tab_container");
+static LLDefaultChildRegistry::Register<LLPlaceHolderPanel> r1("placeholder");
+static LLDefaultChildRegistry::Register<LLTabContainer> r2("tab_container");
+
+LLTabContainer::TabParams::TabParams()
+: tab_top_image_unselected("tab_top_image_unselected"),
+ tab_top_image_selected("tab_top_image_selected"),
+ tab_top_image_flash("tab_top_image_flash"),
+ tab_bottom_image_unselected("tab_bottom_image_unselected"),
+ tab_bottom_image_selected("tab_bottom_image_selected"),
+ tab_bottom_image_flash("tab_bottom_image_flash"),
+ tab_left_image_unselected("tab_left_image_unselected"),
+ tab_left_image_selected("tab_left_image_selected"),
+ tab_left_image_flash("tab_left_image_flash")
+{}
LLTabContainer::Params::Params()
: tab_width("tab_width"),
- tab_position("tab_position"),
tab_min_width("tab_min_width"),
tab_max_width("tab_max_width"),
+ tab_height("tab_height"),
+ label_pad_bottom("label_pad_bottom"),
+ label_pad_left("label_pad_left"),
+ tab_position("tab_position"),
hide_tabs("hide_tabs", false),
- tab_top_image_unselected("tab_top_image_unselected"),
- tab_top_image_selected("tab_top_image_selected"),
- tab_bottom_image_unselected("tab_bottom_image_unselected"),
- tab_bottom_image_selected("tab_bottom_image_selected"),
- tab_left_image_unselected("tab_left_image_unselected"),
- tab_left_image_selected("tab_left_image_selected")
+ tab_padding_right("tab_padding_right"),
+ first_tab("first_tab"),
+ middle_tab("middle_tab"),
+ last_tab("last_tab"),
+ use_custom_icon_ctrl("use_custom_icon_ctrl", false),
+ tab_icon_ctrl_pad("tab_icon_ctrl_pad", 0),
+ use_ellipses("use_ellipses"),
+ font_halign("halign")
{
name(std::string("tab_container"));
mouse_opaque = false;
@@ -132,6 +235,9 @@ LLTabContainer::LLTabContainer(const LLTabContainer::Params& p)
mLockedTabCount(0),
mMinTabWidth(0),
mMaxTabWidth(p.tab_max_width),
+ mTabHeight(p.tab_height),
+ mLabelPadBottom(p.label_pad_bottom),
+ mLabelPadLeft(p.label_pad_left),
mPrevArrowBtn(NULL),
mNextArrowBtn(NULL),
mIsVertical( p.tab_position == LEFT ),
@@ -141,12 +247,14 @@ LLTabContainer::LLTabContainer(const LLTabContainer::Params& p)
mRightTabBtnOffset(p.tab_padding_right),
mTotalTabWidth(0),
mTabPosition(p.tab_position),
- mImageTopUnselected(p.tab_top_image_unselected),
- mImageTopSelected(p.tab_top_image_selected),
- mImageBottomUnselected(p.tab_bottom_image_unselected),
- mImageBottomSelected(p.tab_bottom_image_selected),
- mImageLeftUnselected(p.tab_left_image_unselected),
- mImageLeftSelected(p.tab_left_image_selected)
+ mFontHalign(p.font_halign),
+ mFont(p.font),
+ mFirstTabParams(p.first_tab),
+ mMiddleTabParams(p.middle_tab),
+ mLastTabParams(p.last_tab),
+ mCustomIconCtrlUsed(p.use_custom_icon_ctrl),
+ mTabIconCtrlPad(p.tab_icon_ctrl_pad),
+ mUseTabEllipses(p.use_ellipses)
{
static LLUICachedControl<S32> tabcntr_vert_tab_min_width ("UITabCntrVertTabMinWidth", 0);
@@ -189,7 +297,35 @@ void LLTabContainer::reshape(S32 width, S32 height, BOOL called_from_parent)
}
//virtual
-LLView* LLTabContainer::getChildView(const std::string& name, BOOL recurse, BOOL create_if_missing) const
+LLView* LLTabContainer::getChildView(const std::string& name, BOOL recurse) const
+{
+ tuple_list_t::const_iterator itor;
+ for (itor = mTabList.begin(); itor != mTabList.end(); ++itor)
+ {
+ LLPanel *panel = (*itor)->mTabPanel;
+ if (panel->getName() == name)
+ {
+ return panel;
+ }
+ }
+
+ if (recurse)
+ {
+ for (itor = mTabList.begin(); itor != mTabList.end(); ++itor)
+ {
+ LLPanel *panel = (*itor)->mTabPanel;
+ LLView *child = panel->getChildView(name, recurse);
+ if (child)
+ {
+ return child;
+ }
+ }
+ }
+ return LLView::getChildView(name, recurse);
+}
+
+//virtual
+LLView* LLTabContainer::findChildView(const std::string& name, BOOL recurse) const
{
tuple_list_t::const_iterator itor;
for (itor = mTabList.begin(); itor != mTabList.end(); ++itor)
@@ -206,14 +342,14 @@ LLView* LLTabContainer::getChildView(const std::string& name, BOOL recurse, BOOL
for (itor = mTabList.begin(); itor != mTabList.end(); ++itor)
{
LLPanel *panel = (*itor)->mTabPanel;
- LLView *child = panel->getChildView(name, recurse, FALSE);
+ LLView *child = panel->findChildView(name, recurse);
if (child)
{
return child;
}
}
}
- return LLView::getChildView(name, recurse, create_if_missing);
+ return LLView::findChildView(name, recurse);
}
bool LLTabContainer::addChild(LLView* view, S32 tab_group)
@@ -222,8 +358,6 @@ bool LLTabContainer::addChild(LLView* view, S32 tab_group)
if (panelp)
{
- panelp->setSaveToXML(TRUE);
-
addTabPanel(TabPanelParams().panel(panelp).label(panelp->getLabel()).is_placeholder(dynamic_cast<LLPlaceHolderPanel*>(view) != NULL));
return true;
}
@@ -274,7 +408,7 @@ void LLTabContainer::draw()
setScrollPosPixels((S32)lerp((F32)getScrollPosPixels(), (F32)target_pixel_scroll, LLCriticalDamp::getInterpolant(0.08f)));
- BOOL has_scroll_arrows = (mMaxScrollPos > 0) || (mScrollPosPixels > 0);
+ BOOL has_scroll_arrows = !getTabsHidden() && ((mMaxScrollPos > 0) || (mScrollPosPixels > 0));
if (!mIsVertical)
{
mJumpPrevArrowBtn->setVisible( has_scroll_arrows );
@@ -297,13 +431,22 @@ void LLTabContainer::draw()
}
// Hide all the buttons
- for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
+ if (getTabsHidden())
{
- LLTabTuple* tuple = *iter;
- tuple->mButton->setVisible( FALSE );
+ for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
+ {
+ LLTabTuple* tuple = *iter;
+ tuple->mButton->setVisible( FALSE );
+ }
}
- LLPanel::draw();
+ {
+ LLRect clip_rect = getLocalRect();
+ clip_rect.mLeft+=(LLPANEL_BORDER_WIDTH + 2);
+ clip_rect.mRight-=(LLPANEL_BORDER_WIDTH + 2);
+ LLLocalClipRect clip(clip_rect);
+ LLPanel::draw();
+ }
// if tabs are hidden, don't draw them and leave them in the invisible state
if (!getTabsHidden())
@@ -315,24 +458,6 @@ void LLTabContainer::draw()
tuple->mButton->setVisible( TRUE );
}
- // Draw some of the buttons...
- LLRect clip_rect = getLocalRect();
- if (has_scroll_arrows)
- {
- // ...but clip them.
- if (mIsVertical)
- {
- clip_rect.mBottom = mNextArrowBtn->getRect().mTop + 3*tabcntrv_pad;
- clip_rect.mTop = mPrevArrowBtn->getRect().mBottom - 3*tabcntrv_pad;
- }
- else
- {
- clip_rect.mLeft = mPrevArrowBtn->getRect().mRight;
- clip_rect.mRight = mNextArrowBtn->getRect().mLeft;
- }
- }
- LLLocalClipRect clip(clip_rect);
-
S32 max_scroll_visible = getTabCount() - getMaxScrollPos() + getScrollPos();
S32 idx = 0;
for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
@@ -361,12 +486,6 @@ void LLTabContainer::draw()
}
}
}
- LLUI::pushMatrix();
- {
- LLUI::translate((F32)tuple->mButton->getRect().mLeft, (F32)tuple->mButton->getRect().mBottom, 0.f);
- tuple->mButton->draw();
- }
- LLUI::popMatrix();
idx++;
}
@@ -375,15 +494,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();
}
}
@@ -397,7 +516,7 @@ BOOL LLTabContainer::handleMouseDown( S32 x, S32 y, MASK mask )
{
static LLUICachedControl<S32> tabcntrv_pad ("UITabCntrvPad", 0);
BOOL handled = FALSE;
- BOOL has_scroll_arrows = (getMaxScrollPos() > 0);
+ BOOL has_scroll_arrows = (getMaxScrollPos() > 0) && !getTabsHidden();
if (has_scroll_arrows)
{
@@ -456,7 +575,7 @@ BOOL LLTabContainer::handleMouseDown( S32 x, S32 y, MASK mask )
index = llclamp(index, 0, tab_count-1);
LLButton* tab_button = getTab(index)->mButton;
gFocusMgr.setMouseCapture(this);
- gFocusMgr.setKeyboardFocus(tab_button);
+ tab_button->setFocus(TRUE);
}
}
return handled;
@@ -466,7 +585,7 @@ BOOL LLTabContainer::handleMouseDown( S32 x, S32 y, MASK mask )
BOOL LLTabContainer::handleHover( S32 x, S32 y, MASK mask )
{
BOOL handled = FALSE;
- BOOL has_scroll_arrows = (getMaxScrollPos() > 0);
+ BOOL has_scroll_arrows = (getMaxScrollPos() > 0) && !getTabsHidden();
if (has_scroll_arrows)
{
@@ -508,7 +627,7 @@ BOOL LLTabContainer::handleHover( S32 x, S32 y, MASK mask )
BOOL LLTabContainer::handleMouseUp( S32 x, S32 y, MASK mask )
{
BOOL handled = FALSE;
- BOOL has_scroll_arrows = (getMaxScrollPos() > 0);
+ BOOL has_scroll_arrows = (getMaxScrollPos() > 0) && !getTabsHidden();
if (has_scroll_arrows)
{
@@ -561,10 +680,10 @@ BOOL LLTabContainer::handleMouseUp( S32 x, S32 y, MASK mask )
}
// virtual
-BOOL LLTabContainer::handleToolTip( S32 x, S32 y, std::string& msg, LLRect* sticky_rect )
+BOOL LLTabContainer::handleToolTip( S32 x, S32 y, MASK mask)
{
static LLUICachedControl<S32> tabcntrv_pad ("UITabCntrvPad", 0);
- BOOL handled = LLPanel::handleToolTip( x, y, msg, sticky_rect );
+ BOOL handled = LLPanel::handleToolTip( x, y, mask);
if (!handled && getTabCount() > 0)
{
LLTabTuple* firsttuple = getTab(0);
@@ -594,19 +713,13 @@ BOOL LLTabContainer::handleToolTip( S32 x, S32 y, std::string& msg, LLRect* stic
tuple->mButton->setVisible( TRUE );
S32 local_x = x - tuple->mButton->getRect().mLeft;
S32 local_y = y - tuple->mButton->getRect().mBottom;
- handled = tuple->mButton->handleToolTip( local_x, local_y, msg, sticky_rect );
+ handled = tuple->mButton->handleToolTip( local_x, local_y, mask);
if( handled )
{
break;
}
}
}
-
- for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
- {
- LLTabTuple* tuple = *iter;
- tuple->mButton->setVisible( FALSE );
- }
}
return handled;
}
@@ -702,7 +815,7 @@ BOOL LLTabContainer::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EDrag
{
BOOL has_scroll_arrows = (getMaxScrollPos() > 0);
- if( mDragAndDropDelayTimer.getElapsedTimeF32() > SCROLL_DELAY_TIME )
+ if( mDragAndDropDelayTimer.getStarted() && mDragAndDropDelayTimer.getElapsedTimeF32() > SCROLL_DELAY_TIME )
{
if (has_scroll_arrows)
{
@@ -754,9 +867,39 @@ void LLTabContainer::addTabPanel(LLPanel* panelp)
addTabPanel(TabPanelParams().panel(panelp));
}
+// function to update images
+void LLTabContainer::update_images(LLTabTuple* tuple, TabParams params, LLTabContainer::TabPosition pos)
+{
+ if (tuple && tuple->mButton)
+ {
+ if (pos == LLTabContainer::TOP)
+ {
+ tuple->mButton->setImageUnselected(static_cast<LLUIImage*>(params.tab_top_image_unselected));
+ tuple->mButton->setImageSelected(static_cast<LLUIImage*>(params.tab_top_image_selected));
+ tuple->mButton->setImageFlash(static_cast<LLUIImage*>(params.tab_top_image_flash));
+ }
+ else if (pos == LLTabContainer::BOTTOM)
+ {
+ tuple->mButton->setImageUnselected(static_cast<LLUIImage*>(params.tab_bottom_image_unselected));
+ tuple->mButton->setImageSelected(static_cast<LLUIImage*>(params.tab_bottom_image_selected));
+ tuple->mButton->setImageFlash(static_cast<LLUIImage*>(params.tab_bottom_image_flash));
+ }
+ else if (pos == LLTabContainer::LEFT)
+ {
+ tuple->mButton->setImageUnselected(static_cast<LLUIImage*>(params.tab_left_image_unselected));
+ tuple->mButton->setImageSelected(static_cast<LLUIImage*>(params.tab_left_image_selected));
+ tuple->mButton->setImageFlash(static_cast<LLUIImage*>(params.tab_left_image_flash));
+ }
+ }
+}
+
void LLTabContainer::addTabPanel(const TabPanelParams& panel)
{
LLPanel* child = panel.panel();
+
+ llassert(child);
+ if (!child) return;
+
const std::string& label = panel.label.isProvided()
? panel.label()
: panel.panel()->getLabel();
@@ -767,15 +910,12 @@ void LLTabContainer::addTabPanel(const TabPanelParams& panel)
static LLUICachedControl<S32> tabcntrv_pad ("UITabCntrvPad", 0);
static LLUICachedControl<S32> tabcntr_button_panel_overlap ("UITabCntrButtonPanelOverlap", 0);
- static LLUICachedControl<S32> tabcntr_tab_height ("UITabCntrTabHeight", 0);
static LLUICachedControl<S32> tab_padding ("UITabPadding", 0);
if (child->getParent() == this)
{
// already a child of mine
return;
}
- const LLFontGL* font =
- (mIsVertical ? LLFontGL::getFontSansSerif() : LLFontGL::getFontSansSerifSmall());
// Store the original label for possible xml export.
child->setLabel(label);
@@ -785,28 +925,37 @@ void LLTabContainer::addTabPanel(const TabPanelParams& panel)
S32 button_width = mMinTabWidth;
if (!mIsVertical)
{
- button_width = llclamp(font->getWidth(trimmed_label) + tab_padding, mMinTabWidth, mMaxTabWidth);
+ button_width = llclamp(mFont->getWidth(trimmed_label) + tab_padding, mMinTabWidth, mMaxTabWidth);
}
// Tab panel
S32 tab_panel_top;
S32 tab_panel_bottom;
- if( getTabPosition() == LLTabContainer::TOP )
+ if (!getTabsHidden())
{
- S32 tab_height = mIsVertical ? BTN_HEIGHT : tabcntr_tab_height;
- tab_panel_top = getRect().getHeight() - getTopBorderHeight() - (tab_height - tabcntr_button_panel_overlap);
- tab_panel_bottom = LLPANEL_BORDER_WIDTH;
+ if( getTabPosition() == LLTabContainer::TOP )
+ {
+ S32 tab_height = mIsVertical ? BTN_HEIGHT : mTabHeight;
+ tab_panel_top = getRect().getHeight() - getTopBorderHeight() - (tab_height - tabcntr_button_panel_overlap);
+ tab_panel_bottom = LLPANEL_BORDER_WIDTH;
+ }
+ else
+ {
+ tab_panel_top = getRect().getHeight() - getTopBorderHeight();
+ tab_panel_bottom = (mTabHeight - tabcntr_button_panel_overlap); // Run to the edge, covering up the border
+ }
}
else
{
- tab_panel_top = getRect().getHeight() - getTopBorderHeight();
- tab_panel_bottom = (tabcntr_tab_height - tabcntr_button_panel_overlap); // Run to the edge, covering up the border
+ //Scip tab button space if they are invisible(EXT - 576)
+ tab_panel_top = getRect().getHeight();
+ tab_panel_bottom = LLPANEL_BORDER_WIDTH;
}
-
+
LLRect tab_panel_rect;
- if (mIsVertical)
+ 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);
@@ -842,78 +991,95 @@ void LLTabContainer::addTabPanel(const TabPanelParams& panel)
}
else if( getTabPosition() == LLTabContainer::TOP )
{
- btn_rect.setLeftTopAndSize( 0, getRect().getHeight() - getTopBorderHeight() + tab_fudge, button_width, tabcntr_tab_height );
- tab_img = mImageTopUnselected.get();
- tab_selected_img = mImageTopSelected.get();
+ btn_rect.setLeftTopAndSize( 0, getRect().getHeight() - getTopBorderHeight() + tab_fudge, button_width, mTabHeight);
+ tab_img = mMiddleTabParams.tab_top_image_unselected;
+ tab_selected_img = mMiddleTabParams.tab_top_image_selected;
}
else
{
- btn_rect.setOriginAndSize( 0, 0 + tab_fudge, button_width, tabcntr_tab_height );
- tab_img = mImageBottomUnselected.get();
- tab_selected_img = mImageBottomSelected.get();
+ btn_rect.setOriginAndSize( 0, 0 + tab_fudge, button_width, mTabHeight);
+ tab_img = mMiddleTabParams.tab_bottom_image_unselected;
+ tab_selected_img = mMiddleTabParams.tab_bottom_image_selected;
}
LLTextBox* textbox = NULL;
LLButton* btn = NULL;
+ LLCustomButtonIconCtrl::Params custom_btn_params;
+ {
+ custom_btn_params.icon_ctrl_pad(mTabIconCtrlPad);
+ }
+ LLButton::Params normal_btn_params;
if (placeholder)
{
- btn_rect.translate(0, -LLBUTTON_V_PAD-2);
+ btn_rect.translate(0, -6); // *TODO: make configurable
LLTextBox::Params params;
params.name(trimmed_label);
params.rect(btn_rect);
- params.text(trimmed_label);
- params.font(font);
+ params.initial_value(trimmed_label);
+ params.font(mFont);
textbox = LLUICtrlFactory::create<LLTextBox> (params);
LLButton::Params p;
- p.name("");
+ p.name("placeholder");
btn = LLUICtrlFactory::create<LLButton>(p);
}
else
{
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);
p.click_callback.function(boost::bind(&LLTabContainer::onTabBtn, this, _2, child));
- p.font(font);
+ p.font(mFont);
p.label(trimmed_label);
- p.image_unselected(mImageLeftUnselected);
- p.image_selected(mImageLeftSelected);
+ p.image_unselected(mMiddleTabParams.tab_left_image_unselected);
+ p.image_selected(mMiddleTabParams.tab_left_image_selected);
p.scale_image(true);
- p.font_halign = LLFontGL::LEFT;
+ p.font_halign = mFontHalign;
+ p.pad_bottom( mLabelPadBottom );
p.tab_stop(false);
+ p.label_shadow(false);
if (indent)
{
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
{
- std::string tooltip = trimmed_label;
- tooltip += "\nAlt-Left arrow for previous tab";
- tooltip += "\nAlt-Right arrow for next tab";
-
- 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));
- p.font(font);
+ p.font(mFont);
p.label(trimmed_label);
p.visible(false);
- p.tool_tip(tooltip);
p.scale_image(true);
p.image_unselected(tab_img);
p.image_selected(tab_selected_img);
p.tab_stop(false);
+ p.label_shadow(false);
// Try to squeeze in a bit more text
- p.pad_left(4);
+ p.pad_left( mLabelPadLeft );
p.pad_right(2);
- p.font_halign = LLFontGL::LEFT;
+ p.pad_bottom( mLabelPadBottom );
+ p.font_halign = mFontHalign;
p.follows.flags = FOLLOWS_LEFT;
p.follows.flags = FOLLOWS_LEFT;
@@ -931,27 +1097,67 @@ 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);
+ }
}
}
LLTabTuple* tuple = new LLTabTuple( this, child, btn, textbox );
insertTuple( tuple, insertion_point );
- if (textbox)
+ // if new tab was added as a first or last tab, update button image
+ // and update button image of any tab it may have affected
+ if (tuple == mTabList.front())
+ {
+ update_images(tuple, mFirstTabParams, getTabPosition());
+
+ if (mTabList.size() == 2)
+ {
+ update_images(mTabList[1], mLastTabParams, getTabPosition());
+ }
+ else if (mTabList.size() > 2)
+ {
+ update_images(mTabList[1], mMiddleTabParams, getTabPosition());
+ }
+ }
+ else if (tuple == mTabList.back())
{
- textbox->setSaveToXML(false);
- addChild( textbox, 0 );
+ update_images(tuple, mLastTabParams, getTabPosition());
+
+ if (mTabList.size() > 2)
+ {
+ update_images(mTabList[mTabList.size()-2], mMiddleTabParams, getTabPosition());
+ }
}
- if (btn)
+
+ //Don't add button and textbox if tab buttons are invisible(EXT - 576)
+ if (!getTabsHidden())
{
- btn->setSaveToXML(false);
- addChild( btn, 0 );
+ if (textbox)
+ {
+ addChild( textbox, 0 );
+ }
+ if (btn)
+ {
+ addChild( btn, 0 );
+ }
}
+
if (child)
{
LLUICtrl::addChild(child, 1);
}
+
+ sendChildToFront(mPrevArrowBtn);
+ sendChildToFront(mNextArrowBtn);
+ sendChildToFront(mJumpPrevArrowBtn);
+ sendChildToFront(mJumpNextArrowBtn);
if( select )
{
@@ -1014,7 +1220,17 @@ void LLTabContainer::removeTabPanel(LLPanel* child)
LLTabTuple* tuple = *iter;
if( tuple->mTabPanel == child )
{
- removeChild( tuple->mButton );
+ // update tab button images if removing the first or last tab
+ if ((tuple == mTabList.front()) && (mTabList.size() > 1))
+ {
+ update_images(mTabList[1], mFirstTabParams, getTabPosition());
+ }
+ else if ((tuple == mTabList.back()) && (mTabList.size() > 2))
+ {
+ update_images(mTabList[mTabList.size()-2], mLastTabParams, getTabPosition());
+ }
+
+ removeChild( tuple->mButton );
delete tuple->mButton;
removeChild( tuple->mTabPanel );
@@ -1249,12 +1465,12 @@ BOOL LLTabContainer::selectTab(S32 which)
cbdata = selected_tuple->mTabPanel->getName();
BOOL res = FALSE;
- if( mValidateSignal( this, cbdata ) )
+ if( !mValidateSignal || (*mValidateSignal)( this, cbdata ) )
{
res = setTab(which);
- if (res)
+ if (res && mCommitSignal)
{
- mCommitSignal(this, cbdata);
+ (*mCommitSignal)(this, cbdata);
}
}
@@ -1281,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 );
@@ -1386,33 +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);
+ }
+}
- if (!mIsVertical)
- {
- const LLFontGL* fontp = LLFontGL::getFontSansSerifSmall();
- // remove current width from total tab strip width
- mTotalTabWidth -= tuple->mButton->getRect().getWidth();
+void LLTabContainer::setTabImage(LLPanel* child, const LLUUID& image_id, const LLColor4& color)
+{
+ LLTabTuple* tuple = getTabByPanel(child);
+ if( tuple )
+ {
+ tuple->mButton->setImageOverlay(image_id, LLFontGL::LEFT, color);
+ reshapeTuple(tuple);
+ }
+}
+
+void LLTabContainer::setTabImage(LLPanel* child, LLIconCtrl* icon)
+{
+ LLTabTuple* tuple = getTabByPanel(child);
+ LLCustomButtonIconCtrl* button;
- 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(fontp->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();
}
}
@@ -1475,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 )
@@ -1576,23 +1835,23 @@ void LLTabContainer::initButtons()
S32 btn_top = (getTabPosition() == TOP ) ? getRect().getHeight() - getTopBorderHeight() : tabcntr_arrow_btn_size + 1;
LLRect left_arrow_btn_rect;
- left_arrow_btn_rect.setLeftTopAndSize( LLPANEL_BORDER_WIDTH+1+tabcntr_arrow_btn_size, btn_top + arrow_fudge, tabcntr_arrow_btn_size, tabcntr_arrow_btn_size );
+ left_arrow_btn_rect.setLeftTopAndSize( LLPANEL_BORDER_WIDTH+1+tabcntr_arrow_btn_size, btn_top + arrow_fudge, tabcntr_arrow_btn_size, mTabHeight );
LLRect jump_left_arrow_btn_rect;
- jump_left_arrow_btn_rect.setLeftTopAndSize( LLPANEL_BORDER_WIDTH+1, btn_top + arrow_fudge, tabcntr_arrow_btn_size, tabcntr_arrow_btn_size );
+ jump_left_arrow_btn_rect.setLeftTopAndSize( LLPANEL_BORDER_WIDTH+1, btn_top + arrow_fudge, tabcntr_arrow_btn_size, mTabHeight );
S32 right_pad = tabcntr_arrow_btn_size + LLPANEL_BORDER_WIDTH + 1;
LLRect right_arrow_btn_rect;
right_arrow_btn_rect.setLeftTopAndSize( getRect().getWidth() - mRightTabBtnOffset - right_pad - tabcntr_arrow_btn_size,
btn_top + arrow_fudge,
- tabcntr_arrow_btn_size, tabcntr_arrow_btn_size );
+ tabcntr_arrow_btn_size, mTabHeight );
LLRect jump_right_arrow_btn_rect;
jump_right_arrow_btn_rect.setLeftTopAndSize( getRect().getWidth() - mRightTabBtnOffset - right_pad,
btn_top + arrow_fudge,
- tabcntr_arrow_btn_size, tabcntr_arrow_btn_size );
+ tabcntr_arrow_btn_size, mTabHeight );
LLButton::Params p;
p.name(std::string("Jump Left Arrow"));
@@ -1652,24 +1911,20 @@ void LLTabContainer::initButtons()
}
}
- mPrevArrowBtn->setSaveToXML(false);
mPrevArrowBtn->setTabStop(FALSE);
addChild(mPrevArrowBtn);
- mNextArrowBtn->setSaveToXML(false);
mNextArrowBtn->setTabStop(FALSE);
addChild(mNextArrowBtn);
if (mJumpPrevArrowBtn)
{
- mJumpPrevArrowBtn->setSaveToXML(false);
mJumpPrevArrowBtn->setTabStop(FALSE);
addChild(mJumpPrevArrowBtn);
}
if (mJumpNextArrowBtn)
{
- mJumpNextArrowBtn->setSaveToXML(false);
mJumpNextArrowBtn->setTabStop(FALSE);
addChild(mJumpNextArrowBtn);
}
@@ -1794,12 +2049,11 @@ void LLTabContainer::updateMaxScrollPos()
void LLTabContainer::commitHoveredButton(S32 x, S32 y)
{
- if (hasMouseCapture())
+ if (!getTabsHidden() && hasMouseCapture())
{
for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
{
LLTabTuple* tuple = *iter;
- tuple->mButton->setVisible( TRUE );
S32 local_x = x - tuple->mButton->getRect().mLeft;
S32 local_y = y - tuple->mButton->getRect().mBottom;
if (tuple->mButton->pointInView(local_x, local_y) && tuple->mButton->getEnabled() && !tuple->mTabPanel->getVisible())
@@ -1809,6 +2063,3 @@ void LLTabContainer::commitHoveredButton(S32 x, S32 y)
}
}
}
-
-
-
diff --git a/indra/llui/lltabcontainer.h b/indra/llui/lltabcontainer.h
index ac8232bbb1..eaa2fd54e0 100644
--- a/indra/llui/lltabcontainer.h
+++ b/indra/llui/lltabcontainer.h
@@ -2,31 +2,25 @@
* @file lltabcontainer.h
* @brief LLTabContainer class
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -36,6 +30,8 @@
#include "llpanel.h"
#include "lltextbox.h"
#include "llframetimer.h"
+#include "lliconctrl.h"
+#include "llbutton.h"
class LLTabTuple;
@@ -61,22 +57,57 @@ public:
static void declareValues();
};
+ struct TabParams : public LLInitParam::Block<TabParams>
+ {
+ Optional<LLUIImage*> tab_top_image_unselected,
+ tab_top_image_selected,
+ tab_top_image_flash,
+ tab_bottom_image_unselected,
+ tab_bottom_image_selected,
+ tab_bottom_image_flash,
+ tab_left_image_unselected,
+ tab_left_image_selected,
+ tab_left_image_flash;
+ TabParams();
+ };
+
struct Params
: public LLInitParam::Block<Params, LLPanel::Params>
{
Optional<TabPosition, TabPositions> tab_position;
Optional<S32> tab_width,
tab_min_width,
- tab_max_width;
+ tab_max_width,
+ tab_height,
+ label_pad_bottom,
+ label_pad_left;
+
Optional<bool> hide_tabs;
Optional<S32> tab_padding_right;
- Optional<LLUIImage*> tab_top_image_unselected,
- tab_top_image_selected,
- tab_bottom_image_unselected,
- tab_bottom_image_selected,
- tab_left_image_unselected,
- tab_left_image_selected;
+ Optional<TabParams> first_tab,
+ 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();
};
@@ -99,12 +130,13 @@ public:
/*virtual*/ BOOL handleMouseDown( S32 x, S32 y, MASK mask );
/*virtual*/ BOOL handleHover( S32 x, S32 y, MASK mask );
/*virtual*/ BOOL handleMouseUp( S32 x, S32 y, MASK mask );
- /*virtual*/ BOOL handleToolTip(S32 x, S32 y, std::string& msg, LLRect* sticky_rect );
+ /*virtual*/ BOOL handleToolTip(S32 x, S32 y, MASK mask);
/*virtual*/ BOOL handleKeyHere(KEY key, MASK mask);
/*virtual*/ BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
EDragAndDropType type, void* cargo_data,
EAcceptance* accept, std::string& tooltip);
- /*virtual*/ LLView* getChildView(const std::string& name, BOOL recurse = TRUE, BOOL create_if_missing = TRUE) const;
+ /*virtual*/ LLView* getChildView(const std::string& name, BOOL recurse = TRUE) const;
+ /*virtual*/ LLView* findChildView(const std::string& name, BOOL recurse = TRUE) const;
/*virtual*/ void initFromParams(const LLPanel::Params& p);
/*virtual*/ bool addChild(LLView* view, S32 tab_group = 0);
/*virtual*/ BOOL postBuild();
@@ -122,6 +154,10 @@ public:
TabPanelParams()
: panel("panel", NULL),
+ label("label"),
+ select_tab("select_tab"),
+ is_placeholder("is_placeholder"),
+ indent("indent"),
insert_at("insert_at", END)
{}
};
@@ -155,6 +191,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);
@@ -208,6 +246,10 @@ private:
void updateMaxScrollPos();
void commitHoveredButton(S32 x, S32 y);
+ // updates tab button images given the tuple, tab position and the corresponding params
+ void update_images(LLTabTuple* tuple, TabParams params, LLTabContainer::TabPosition pos);
+ void reshapeTuple(LLTabTuple* tuple);
+
// Variables
typedef std::vector<LLTabTuple*> tuple_list_t;
@@ -241,15 +283,25 @@ private:
S32 mMaxTabWidth;
S32 mTotalTabWidth;
+ S32 mTabHeight;
+
+ // Padding under the text labels of tab buttons
+ S32 mLabelPadBottom;
+ // Padding to the left of text labels of tab buttons
+ S32 mLabelPadLeft;
LLFrameTimer mDragAndDropDelayTimer;
+
+ LLFontGL::HAlign mFontHalign;
+ const LLFontGL* mFont;
+
+ TabParams mFirstTabParams;
+ TabParams mMiddleTabParams;
+ TabParams mLastTabParams;
- LLPointer<LLUIImage> mImageTopUnselected;
- LLPointer<LLUIImage> mImageTopSelected;
- LLPointer<LLUIImage> mImageBottomUnselected;
- LLPointer<LLUIImage> mImageBottomSelected;
- LLPointer<LLUIImage> mImageLeftUnselected;
- LLPointer<LLUIImage> mImageLeftSelected;
+ bool mCustomIconCtrlUsed;
+ S32 mTabIconCtrlPad;
+ bool mUseTabEllipses;
};
#endif // LL_TABCONTAINER_H
diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp
new file mode 100644
index 0000000000..574b24cf13
--- /dev/null
+++ b/indra/llui/lltextbase.cpp
@@ -0,0 +1,2901 @@
+/**
+ * @file lltextbase.cpp
+ * @author Martin Reddy
+ * @brief The base class of text box/editor, providing Url handling support
+ *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "lltextbase.h"
+
+#include "lllocalcliprect.h"
+#include "llmenugl.h"
+#include "llscrollcontainer.h"
+#include "llstl.h"
+#include "lltextparser.h"
+#include "lltextutil.h"
+#include "lltooltip.h"
+#include "lluictrl.h"
+#include "llurlaction.h"
+#include "llurlregistry.h"
+#include "llview.h"
+#include "llwindow.h"
+#include <boost/bind.hpp>
+
+const F32 CURSOR_FLASH_DELAY = 1.0f; // in seconds
+const S32 CURSOR_THICKNESS = 2;
+
+LLTextBase::line_info::line_info(S32 index_start, S32 index_end, LLRect rect, S32 line_num)
+: mDocIndexStart(index_start),
+ mDocIndexEnd(index_end),
+ mRect(rect),
+ mLineNum(line_num)
+{}
+
+bool LLTextBase::compare_segment_end::operator()(const LLTextSegmentPtr& a, const LLTextSegmentPtr& b) const
+{
+ // sort empty spans (e.g. 11-11) after previous non-empty spans (e.g. 5-11)
+ if (a->getEnd() == b->getEnd())
+ {
+ return a->getStart() < b->getStart();
+ }
+ else
+ {
+ return a->getEnd() < b->getEnd();
+ }
+}
+
+
+// helper functors
+struct LLTextBase::compare_bottom
+{
+ bool operator()(const S32& a, const LLTextBase::line_info& b) const
+ {
+ return a > b.mRect.mBottom; // bottom of a is higher than bottom of b
+ }
+
+ bool operator()(const LLTextBase::line_info& a, const S32& b) const
+ {
+ return a.mRect.mBottom > b; // bottom of a is higher than bottom of b
+ }
+
+ bool operator()(const LLTextBase::line_info& a, const LLTextBase::line_info& b) const
+ {
+ return a.mRect.mBottom > b.mRect.mBottom; // bottom of a is higher than bottom of b
+ }
+
+};
+
+// helper functors
+struct LLTextBase::compare_top
+{
+ bool operator()(const S32& a, const LLTextBase::line_info& b) const
+ {
+ return a > b.mRect.mTop; // top of a is higher than top of b
+ }
+
+ bool operator()(const LLTextBase::line_info& a, const S32& b) const
+ {
+ return a.mRect.mTop > b; // top of a is higher than top of b
+ }
+
+ bool operator()(const LLTextBase::line_info& a, const LLTextBase::line_info& b) const
+ {
+ return a.mRect.mTop > b.mRect.mTop; // top of a is higher than top of b
+ }
+};
+
+struct LLTextBase::line_end_compare
+{
+ bool operator()(const S32& pos, const LLTextBase::line_info& info) const
+ {
+ return (pos < info.mDocIndexEnd);
+ }
+
+ bool operator()(const LLTextBase::line_info& info, const S32& pos) const
+ {
+ return (info.mDocIndexEnd < pos);
+ }
+
+ bool operator()(const LLTextBase::line_info& a, const LLTextBase::line_info& b) const
+ {
+ return (a.mDocIndexEnd < b.mDocIndexEnd);
+ }
+
+};
+
+//////////////////////////////////////////////////////////////////////////
+//
+// LLTextBase
+//
+
+// register LLTextBase::Params under name "textbase"
+static LLWidgetNameRegistry::StaticRegistrar sRegisterTextBaseParams(&typeid(LLTextBase::Params), "textbase");
+
+LLTextBase::LineSpacingParams::LineSpacingParams()
+: multiple("multiple", 1.f),
+ pixels("pixels", 0)
+{
+}
+
+
+LLTextBase::Params::Params()
+: cursor_color("cursor_color"),
+ text_color("text_color"),
+ text_readonly_color("text_readonly_color"),
+ bg_visible("bg_visible", false),
+ border_visible("border_visible", false),
+ bg_readonly_color("bg_readonly_color"),
+ bg_writeable_color("bg_writeable_color"),
+ bg_focus_color("bg_focus_color"),
+ text_selected_color("text_selected_color"),
+ bg_selected_color("bg_selected_color"),
+ allow_scroll("allow_scroll", true),
+ plain_text("plain_text",false),
+ track_end("track_end", false),
+ read_only("read_only", false),
+ v_pad("v_pad", 0),
+ h_pad("h_pad", 0),
+ clip_partial("clip_partial", true),
+ line_spacing("line_spacing"),
+ max_text_length("max_length", 255),
+ font_shadow("font_shadow"),
+ wrap("wrap"),
+ use_ellipses("use_ellipses", false),
+ parse_urls("parse_urls", false),
+ parse_highlights("parse_highlights", false)
+{
+ addSynonym(track_end, "track_bottom");
+ addSynonym(wrap, "word_wrap");
+ addSynonym(parse_urls, "allow_html");
+}
+
+
+LLTextBase::LLTextBase(const LLTextBase::Params &p)
+: LLUICtrl(p, LLTextViewModelPtr(new LLTextViewModel)),
+ mURLClickSignal(NULL),
+ mMaxTextByteLength( p.max_text_length ),
+ mDefaultFont(p.font),
+ mFontShadow(p.font_shadow),
+ mPopupMenu(NULL),
+ mReadOnly(p.read_only),
+ mCursorColor(p.cursor_color),
+ mFgColor(p.text_color),
+ mBorderVisible( p.border_visible ),
+ mReadOnlyFgColor(p.text_readonly_color),
+ mWriteableBgColor(p.bg_writeable_color),
+ mReadOnlyBgColor(p.bg_readonly_color),
+ mFocusBgColor(p.bg_focus_color),
+ mTextSelectedColor(p.text_selected_color),
+ mSelectedBGColor(p.bg_selected_color),
+ mReflowIndex(S32_MAX),
+ mCursorPos( 0 ),
+ mScrollNeeded(FALSE),
+ mDesiredXPixel(-1),
+ mHPad(p.h_pad),
+ mVPad(p.v_pad),
+ mHAlign(p.font_halign),
+ mVAlign(p.font_valign),
+ mLineSpacingMult(p.line_spacing.multiple),
+ mLineSpacingPixels(p.line_spacing.pixels),
+ mClipPartial(p.clip_partial && !p.allow_scroll),
+ mTrackEnd( p.track_end ),
+ mScrollIndex(-1),
+ mSelectionStart( 0 ),
+ mSelectionEnd( 0 ),
+ mIsSelecting( FALSE ),
+ mPlainText ( p.plain_text ),
+ mWordWrap(p.wrap),
+ mUseEllipses( p.use_ellipses ),
+ mParseHTML(p.parse_urls),
+ mParseHighlights(p.parse_highlights),
+ mBGVisible(p.bg_visible),
+ mScroller(NULL),
+ mStyleDirty(true)
+{
+ if(p.allow_scroll)
+ {
+ LLScrollContainer::Params scroll_params;
+ scroll_params.name = "text scroller";
+ scroll_params.rect = getLocalRect();
+ scroll_params.follows.flags = FOLLOWS_ALL;
+ scroll_params.is_opaque = false;
+ scroll_params.mouse_opaque = false;
+ scroll_params.min_auto_scroll_rate = 200;
+ scroll_params.max_auto_scroll_rate = 800;
+ scroll_params.border_visible = p.border_visible;
+ mScroller = LLUICtrlFactory::create<LLScrollContainer>(scroll_params);
+ addChild(mScroller);
+ }
+
+ LLView::Params view_params;
+ view_params.name = "text_contents";
+ view_params.rect = LLRect(0, 500, 500, 0);
+ view_params.mouse_opaque = false;
+
+ mDocumentView = LLUICtrlFactory::create<LLView>(view_params);
+ if (mScroller)
+ {
+ mScroller->addChild(mDocumentView);
+ }
+ else
+ {
+ addChild(mDocumentView);
+ }
+
+ createDefaultSegment();
+
+ updateRects();
+}
+
+LLTextBase::~LLTextBase()
+{
+ mSegments.clear();
+ delete mURLClickSignal;
+}
+
+void LLTextBase::initFromParams(const LLTextBase::Params& p)
+{
+ LLUICtrl::initFromParams(p);
+ resetDirty(); // Update saved text state
+ updateSegments();
+
+ // HACK: work around enabled == readonly design bug -- RN
+ // setEnabled will modify our read only status, so do this after
+ // LLTextBase::initFromParams
+ if (p.read_only.isProvided())
+ {
+ mReadOnly = p.read_only;
+ }
+}
+
+bool LLTextBase::truncate()
+{
+ BOOL did_truncate = FALSE;
+
+ // First rough check - if we're less than 1/4th the size, we're OK
+ if (getLength() >= S32(mMaxTextByteLength / 4))
+ {
+ // Have to check actual byte size
+ LLWString text(getWText());
+ S32 utf8_byte_size = wstring_utf8_length(text);
+ if ( utf8_byte_size > mMaxTextByteLength )
+ {
+ // Truncate safely in UTF-8
+ std::string temp_utf8_text = wstring_to_utf8str(text);
+ temp_utf8_text = utf8str_truncate( temp_utf8_text, mMaxTextByteLength );
+ LLWString text = utf8str_to_wstring( temp_utf8_text );
+ // remove extra bit of current string, to preserve formatting, etc.
+ removeStringNoUndo(text.size(), getWText().size() - text.size());
+ did_truncate = TRUE;
+ }
+ }
+
+ return did_truncate;
+}
+
+const LLStyle::Params& LLTextBase::getDefaultStyleParams()
+{
+ //FIXME: convert mDefaultStyle to a flyweight http://www.boost.org/doc/libs/1_40_0/libs/flyweight/doc/index.html
+ //and eliminate color member values
+ if (mStyleDirty)
+ {
+ mDefaultStyle
+ .color(LLUIColor(&mFgColor)) // pass linked color instead of copy of mFGColor
+ .readonly_color(LLUIColor(&mReadOnlyFgColor))
+ .selected_color(LLUIColor(&mTextSelectedColor))
+ .font(mDefaultFont)
+ .drop_shadow(mFontShadow);
+ mStyleDirty = false;
+ }
+ return mDefaultStyle;
+}
+
+void LLTextBase::onValueChange(S32 start, S32 end)
+{
+}
+
+
+// Draws the black box behind the selected text
+void LLTextBase::drawSelectionBackground()
+{
+ // Draw selection even if we don't have keyboard focus for search/replace
+ if( hasSelection() && !mLineInfoList.empty())
+ {
+ std::vector<LLRect> selection_rects;
+
+ S32 selection_left = llmin( mSelectionStart, mSelectionEnd );
+ S32 selection_right = llmax( mSelectionStart, mSelectionEnd );
+ LLRect selection_rect = mVisibleTextRect;
+
+ // Skip through the lines we aren't drawing.
+ LLRect content_display_rect = getVisibleDocumentRect();
+
+ // binary search for line that starts before top of visible buffer
+ line_list_t::const_iterator line_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), content_display_rect.mTop, compare_bottom());
+ line_list_t::const_iterator end_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), content_display_rect.mBottom, compare_top());
+
+ bool done = false;
+
+ // Find the coordinates of the selected area
+ for (;line_iter != end_iter && !done; ++line_iter)
+ {
+ // is selection visible on this line?
+ if (line_iter->mDocIndexEnd > selection_left && line_iter->mDocIndexStart < selection_right)
+ {
+ segment_set_t::iterator segment_iter;
+ S32 segment_offset;
+ getSegmentAndOffset(line_iter->mDocIndexStart, &segment_iter, &segment_offset);
+
+ LLRect selection_rect;
+ selection_rect.mLeft = line_iter->mRect.mLeft;
+ selection_rect.mRight = line_iter->mRect.mLeft;
+ selection_rect.mBottom = line_iter->mRect.mBottom;
+ selection_rect.mTop = line_iter->mRect.mTop;
+
+ for(;segment_iter != mSegments.end(); ++segment_iter, segment_offset = 0)
+ {
+ LLTextSegmentPtr segmentp = *segment_iter;
+
+ S32 segment_line_start = segmentp->getStart() + segment_offset;
+ S32 segment_line_end = llmin(segmentp->getEnd(), line_iter->mDocIndexEnd);
+
+ if (segment_line_start > segment_line_end) break;
+
+ S32 segment_width = 0;
+ S32 segment_height = 0;
+
+ // if selection after beginning of segment
+ if(selection_left >= segment_line_start)
+ {
+ S32 num_chars = llmin(selection_left, segment_line_end) - segment_line_start;
+ segmentp->getDimensions(segment_offset, num_chars, segment_width, segment_height);
+ selection_rect.mLeft += segment_width;
+ }
+
+ // if selection_right == segment_line_end then that means we are the first character of the next segment
+ // or first character of the next line, in either case we want to add the length of the current segment
+ // to the selection rectangle and continue.
+ // if selection right > segment_line_end then selection spans end of current segment...
+ if (selection_right >= segment_line_end)
+ {
+ // extend selection slightly beyond end of line
+ // to indicate selection of newline character (use "n" character to determine width)
+ S32 num_chars = segment_line_end - segment_line_start;
+ segmentp->getDimensions(segment_offset, num_chars, segment_width, segment_height);
+ selection_rect.mRight += segment_width;
+ }
+ // else if selection ends on current segment...
+ else
+ {
+ S32 num_chars = selection_right - segment_line_start;
+ segmentp->getDimensions(segment_offset, num_chars, segment_width, segment_height);
+ selection_rect.mRight += segment_width;
+
+ break;
+ }
+ }
+ selection_rects.push_back(selection_rect);
+ }
+ }
+
+ // Draw the selection box (we're using a box instead of reversing the colors on the selected text).
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+ const LLColor4& color = mSelectedBGColor;
+ F32 alpha = hasFocus() ? 0.7f : 0.3f;
+ alpha *= getDrawContext().mAlpha;
+ LLColor4 selection_color(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE], alpha);
+
+ for (std::vector<LLRect>::iterator rect_it = selection_rects.begin();
+ rect_it != selection_rects.end();
+ ++rect_it)
+ {
+ LLRect selection_rect = *rect_it;
+ selection_rect.translate(mVisibleTextRect.mLeft - content_display_rect.mLeft, mVisibleTextRect.mBottom - content_display_rect.mBottom);
+ gl_rect_2d(selection_rect, selection_color);
+ }
+ }
+}
+
+void LLTextBase::drawCursor()
+{
+ F32 alpha = getDrawContext().mAlpha;
+
+ if( hasFocus()
+ && gFocusMgr.getAppHasFocus()
+ && !mReadOnly)
+ {
+ const LLWString &wtext = getWText();
+ const llwchar* text = wtext.c_str();
+
+ LLRect cursor_rect = getLocalRectFromDocIndex(mCursorPos);
+ cursor_rect.translate(-1, 0);
+ segment_set_t::iterator seg_it = getSegIterContaining(mCursorPos);
+
+ // take style from last segment
+ LLTextSegmentPtr segmentp;
+
+ if (seg_it != mSegments.end())
+ {
+ segmentp = *seg_it;
+ }
+ else
+ {
+ return;
+ }
+
+ // Draw the cursor
+ // (Flash the cursor every half second starting a fixed time after the last keystroke)
+ F32 elapsed = mCursorBlinkTimer.getElapsedTimeF32();
+ if( (elapsed < CURSOR_FLASH_DELAY ) || (S32(elapsed * 2) & 1) )
+ {
+
+ if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode() && !hasSelection())
+ {
+ S32 segment_width = 0;
+ S32 segment_height = 0;
+ segmentp->getDimensions(mCursorPos - segmentp->getStart(), 1, segment_width, segment_height);
+ S32 width = llmax(CURSOR_THICKNESS, segment_width);
+ cursor_rect.mRight = cursor_rect.mLeft + width;
+ }
+ else
+ {
+ cursor_rect.mRight = cursor_rect.mLeft + CURSOR_THICKNESS;
+ }
+
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+
+ LLColor4 cursor_color = mCursorColor.get() % alpha;
+ gGL.color4fv( cursor_color.mV );
+
+ gl_rect_2d(cursor_rect);
+
+ if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode() && !hasSelection() && text[mCursorPos] != '\n')
+ {
+ LLColor4 text_color;
+ const LLFontGL* fontp;
+ text_color = segmentp->getColor();
+ fontp = segmentp->getStyle()->getFont();
+ fontp->render(text, mCursorPos, cursor_rect,
+ LLColor4(1.f - text_color.mV[VRED], 1.f - text_color.mV[VGREEN], 1.f - text_color.mV[VBLUE], alpha),
+ LLFontGL::LEFT, mVAlign,
+ LLFontGL::NORMAL,
+ LLFontGL::NO_SHADOW,
+ 1);
+ }
+
+ // Make sure the IME is in the right place
+ LLRect screen_pos = calcScreenRect();
+ LLCoordGL ime_pos( screen_pos.mLeft + llfloor(cursor_rect.mLeft), screen_pos.mBottom + llfloor(cursor_rect.mTop) );
+
+ ime_pos.mX = (S32) (ime_pos.mX * LLUI::sGLScaleFactor.mV[VX]);
+ ime_pos.mY = (S32) (ime_pos.mY * LLUI::sGLScaleFactor.mV[VY]);
+ getWindow()->setLanguageTextInput( ime_pos );
+ }
+ }
+}
+
+void LLTextBase::drawText()
+{
+ const S32 text_len = getLength();
+ if( text_len <= 0 )
+ {
+ return;
+ }
+ S32 selection_left = -1;
+ S32 selection_right = -1;
+ // Draw selection even if we don't have keyboard focus for search/replace
+ if( hasSelection())
+ {
+ selection_left = llmin( mSelectionStart, mSelectionEnd );
+ selection_right = llmax( mSelectionStart, mSelectionEnd );
+ }
+
+ LLRect scrolled_view_rect = getVisibleDocumentRect();
+ std::pair<S32, S32> line_range = getVisibleLines(mClipPartial);
+ S32 first_line = line_range.first;
+ S32 last_line = line_range.second;
+ if (first_line >= last_line)
+ {
+ return;
+ }
+
+ S32 line_start = getLineStart(first_line);
+ // find first text segment that spans top of visible portion of text buffer
+ segment_set_t::iterator seg_iter = getSegIterContaining(line_start);
+ if (seg_iter == mSegments.end())
+ {
+ return;
+ }
+
+ LLTextSegmentPtr cur_segment = *seg_iter;
+
+ for (S32 cur_line = first_line; cur_line < last_line; cur_line++)
+ {
+ S32 next_line = cur_line + 1;
+ line_info& line = mLineInfoList[cur_line];
+
+ S32 next_start = -1;
+ S32 line_end = text_len;
+
+ if (next_line < getLineCount())
+ {
+ next_start = getLineStart(next_line);
+ line_end = next_start;
+ }
+
+ LLRect text_rect(line.mRect.mLeft + mVisibleTextRect.mLeft - scrolled_view_rect.mLeft,
+ line.mRect.mTop - scrolled_view_rect.mBottom + mVisibleTextRect.mBottom,
+ llmin(mDocumentView->getRect().getWidth(), line.mRect.mRight) - scrolled_view_rect.mLeft,
+ line.mRect.mBottom - scrolled_view_rect.mBottom + mVisibleTextRect.mBottom);
+
+ // draw a single line of text
+ S32 seg_start = line_start;
+ while( seg_start < line_end )
+ {
+ while( cur_segment->getEnd() <= seg_start )
+ {
+ seg_iter++;
+ if (seg_iter == mSegments.end())
+ {
+ llwarns << "Ran off the segmentation end!" << llendl;
+
+ return;
+ }
+ cur_segment = *seg_iter;
+ }
+
+ S32 clipped_end = llmin( line_end, cur_segment->getEnd() ) - cur_segment->getStart();
+
+ if (mUseEllipses // using ellipses
+ && clipped_end == line_end // last segment on line
+ && next_line == last_line // this is the last visible line
+ && last_line < (S32)mLineInfoList.size()) // and there is more text to display
+ {
+ // more lines of text to go, but we can't fit them
+ // so shrink text rect to force ellipses
+ text_rect.mRight -= 2;
+ }
+
+ text_rect.mLeft = (S32)(cur_segment->draw(seg_start - cur_segment->getStart(), clipped_end, selection_left, selection_right, text_rect));
+
+ seg_start = clipped_end + cur_segment->getStart();
+ }
+
+ line_start = next_start;
+ }
+}
+
+///////////////////////////////////////////////////////////////////
+// Returns change in number of characters in mWText
+
+S32 LLTextBase::insertStringNoUndo(S32 pos, const LLWString &wstr, LLTextBase::segment_vec_t* segments )
+{
+ LLWString text(getWText());
+ S32 old_len = text.length(); // length() returns character length
+ S32 insert_len = wstr.length();
+
+ pos = getEditableIndex(pos, true);
+
+ segment_set_t::iterator seg_iter = getSegIterContaining(pos);
+
+ LLTextSegmentPtr default_segment;
+
+ LLTextSegmentPtr segmentp;
+ if (seg_iter != mSegments.end())
+ {
+ segmentp = *seg_iter;
+ }
+ else
+ {
+ //segmentp = mSegments.back();
+ return pos;
+ }
+
+ if (segmentp->canEdit())
+ {
+ segmentp->setEnd(segmentp->getEnd() + insert_len);
+ if (seg_iter != mSegments.end())
+ {
+ ++seg_iter;
+ }
+ }
+ else
+ {
+ // create default editable segment to hold new text
+ LLStyleConstSP sp(new LLStyle(getDefaultStyleParams()));
+ default_segment = new LLNormalTextSegment( sp, pos, pos + insert_len, *this);
+ }
+
+ // shift remaining segments to right
+ for(;seg_iter != mSegments.end(); ++seg_iter)
+ {
+ LLTextSegmentPtr segmentp = *seg_iter;
+ segmentp->setStart(segmentp->getStart() + insert_len);
+ segmentp->setEnd(segmentp->getEnd() + insert_len);
+ }
+
+ // insert new segments
+ if (segments)
+ {
+ if (default_segment.notNull())
+ {
+ // potentially overwritten by segments passed in
+ insertSegment(default_segment);
+ }
+ for (segment_vec_t::iterator seg_iter = segments->begin();
+ seg_iter != segments->end();
+ ++seg_iter)
+ {
+ LLTextSegment* segmentp = *seg_iter;
+ insertSegment(segmentp);
+ }
+ }
+
+ text.insert(pos, wstr);
+ getViewModel()->setDisplay(text);
+
+ if ( truncate() )
+ {
+ insert_len = getLength() - old_len;
+ }
+
+ onValueChange(pos, pos + insert_len);
+ needsReflow(pos);
+
+ return insert_len;
+}
+
+S32 LLTextBase::removeStringNoUndo(S32 pos, S32 length)
+{
+ LLWString text(getWText());
+ segment_set_t::iterator seg_iter = getSegIterContaining(pos);
+ while(seg_iter != mSegments.end())
+ {
+ LLTextSegmentPtr segmentp = *seg_iter;
+ S32 end = pos + length;
+ if (segmentp->getStart() < pos)
+ {
+ // deleting from middle of segment
+ if (segmentp->getEnd() > end)
+ {
+ segmentp->setEnd(segmentp->getEnd() - length);
+ }
+ // truncating segment
+ else
+ {
+ segmentp->setEnd(pos);
+ }
+ }
+ else if (segmentp->getStart() < end)
+ {
+ // deleting entire segment
+ if (segmentp->getEnd() <= end)
+ {
+ // remove segment
+ segmentp->unlinkFromDocument(this);
+ segment_set_t::iterator seg_to_erase(seg_iter++);
+ mSegments.erase(seg_to_erase);
+ continue;
+ }
+ // deleting head of segment
+ else
+ {
+ segmentp->setStart(pos);
+ segmentp->setEnd(segmentp->getEnd() - length);
+ }
+ }
+ else
+ {
+ // shifting segments backward to fill deleted portion
+ segmentp->setStart(segmentp->getStart() - length);
+ segmentp->setEnd(segmentp->getEnd() - length);
+ }
+ ++seg_iter;
+ }
+
+ text.erase(pos, length);
+ getViewModel()->setDisplay(text);
+
+ // recreate default segment in case we erased everything
+ createDefaultSegment();
+
+ onValueChange(pos, pos);
+ needsReflow(pos);
+
+ return -length; // This will be wrong if someone calls removeStringNoUndo with an excessive length
+}
+
+S32 LLTextBase::overwriteCharNoUndo(S32 pos, llwchar wc)
+{
+ if (pos > (S32)getLength())
+ {
+ return 0;
+ }
+ LLWString text(getWText());
+ text[pos] = wc;
+ getViewModel()->setDisplay(text);
+
+ onValueChange(pos, pos + 1);
+ needsReflow(pos);
+
+ return 1;
+}
+
+
+void LLTextBase::createDefaultSegment()
+{
+ // ensures that there is always at least one segment
+ if (mSegments.empty())
+ {
+ LLStyleConstSP sp(new LLStyle(getDefaultStyleParams()));
+ LLTextSegmentPtr default_segment = new LLNormalTextSegment( sp, 0, getLength() + 1, *this);
+ mSegments.insert(default_segment);
+ default_segment->linkToDocument(this);
+ }
+}
+
+void LLTextBase::insertSegment(LLTextSegmentPtr segment_to_insert)
+{
+ if (segment_to_insert.isNull())
+ {
+ return;
+ }
+
+ segment_set_t::iterator cur_seg_iter = getSegIterContaining(segment_to_insert->getStart());
+ S32 reflow_start_index = 0;
+
+ if (cur_seg_iter == mSegments.end())
+ {
+ mSegments.insert(segment_to_insert);
+ segment_to_insert->linkToDocument(this);
+ reflow_start_index = segment_to_insert->getStart();
+ }
+ else
+ {
+ LLTextSegmentPtr cur_segmentp = *cur_seg_iter;
+ reflow_start_index = cur_segmentp->getStart();
+ if (cur_segmentp->getStart() < segment_to_insert->getStart())
+ {
+ S32 old_segment_end = cur_segmentp->getEnd();
+ // split old at start point for new segment
+ cur_segmentp->setEnd(segment_to_insert->getStart());
+ // advance to next segment
+ // insert remainder of old segment
+ LLStyleConstSP sp = cur_segmentp->getStyle();
+ LLTextSegmentPtr remainder_segment = new LLNormalTextSegment( sp, segment_to_insert->getStart(), old_segment_end, *this);
+ mSegments.insert(cur_seg_iter, remainder_segment);
+ remainder_segment->linkToDocument(this);
+ // insert new segment before remainder of old segment
+ mSegments.insert(cur_seg_iter, segment_to_insert);
+
+ segment_to_insert->linkToDocument(this);
+ // at this point, there will be two overlapping segments owning the text
+ // associated with the incoming segment
+ }
+ else
+ {
+ mSegments.insert(cur_seg_iter, segment_to_insert);
+ segment_to_insert->linkToDocument(this);
+ }
+
+ // now delete/truncate remaining segments as necessary
+ // cur_seg_iter points to segment before incoming segment
+ while(cur_seg_iter != mSegments.end())
+ {
+ cur_segmentp = *cur_seg_iter;
+ if (cur_segmentp == segment_to_insert)
+ {
+ ++cur_seg_iter;
+ continue;
+ }
+
+ if (cur_segmentp->getStart() >= segment_to_insert->getStart())
+ {
+ if(cur_segmentp->getEnd() <= segment_to_insert->getEnd())
+ {
+ cur_segmentp->unlinkFromDocument(this);
+ // grab copy of iterator to erase, and bump it
+ segment_set_t::iterator seg_to_erase(cur_seg_iter++);
+ mSegments.erase(seg_to_erase);
+ continue;
+ }
+ else
+ {
+ // last overlapping segment, clip to end of incoming segment
+ // and stop traversal
+ cur_segmentp->setStart(segment_to_insert->getEnd());
+ break;
+ }
+ }
+ ++cur_seg_iter;
+ }
+ }
+
+ // layout potentially changed
+ needsReflow(reflow_start_index);
+}
+
+BOOL LLTextBase::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y);
+ if (cur_segment && cur_segment->handleMouseDown(x, y, mask))
+ {
+ return TRUE;
+ }
+
+ return LLUICtrl::handleMouseDown(x, y, mask);
+}
+
+BOOL LLTextBase::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y);
+ if (cur_segment && cur_segment->handleMouseUp(x, y, mask))
+ {
+ // Did we just click on a link?
+ if (mURLClickSignal
+ && cur_segment->getStyle()
+ && cur_segment->getStyle()->isLink())
+ {
+ // *TODO: send URL here?
+ (*mURLClickSignal)(this, LLSD() );
+ }
+ return TRUE;
+ }
+
+ return LLUICtrl::handleMouseUp(x, y, mask);
+}
+
+BOOL LLTextBase::handleMiddleMouseDown(S32 x, S32 y, MASK mask)
+{
+ LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y);
+ if (cur_segment && cur_segment->handleMiddleMouseDown(x, y, mask))
+ {
+ return TRUE;
+ }
+
+ return LLUICtrl::handleMiddleMouseDown(x, y, mask);
+}
+
+BOOL LLTextBase::handleMiddleMouseUp(S32 x, S32 y, MASK mask)
+{
+ LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y);
+ if (cur_segment && cur_segment->handleMiddleMouseUp(x, y, mask))
+ {
+ return TRUE;
+ }
+
+ return LLUICtrl::handleMiddleMouseUp(x, y, mask);
+}
+
+BOOL LLTextBase::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+ LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y);
+ if (cur_segment && cur_segment->handleRightMouseDown(x, y, mask))
+ {
+ return TRUE;
+ }
+
+ return LLUICtrl::handleRightMouseDown(x, y, mask);
+}
+
+BOOL LLTextBase::handleRightMouseUp(S32 x, S32 y, MASK mask)
+{
+ LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y);
+ if (cur_segment && cur_segment->handleRightMouseUp(x, y, mask))
+ {
+ return TRUE;
+ }
+
+ return LLUICtrl::handleRightMouseUp(x, y, mask);
+}
+
+BOOL LLTextBase::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y);
+ if (cur_segment && cur_segment->handleDoubleClick(x, y, mask))
+ {
+ return TRUE;
+ }
+
+ return LLUICtrl::handleDoubleClick(x, y, mask);
+}
+
+BOOL LLTextBase::handleHover(S32 x, S32 y, MASK mask)
+{
+ LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y);
+ if (cur_segment && cur_segment->handleHover(x, y, mask))
+ {
+ return TRUE;
+ }
+
+ return LLUICtrl::handleHover(x, y, mask);
+}
+
+BOOL LLTextBase::handleScrollWheel(S32 x, S32 y, S32 clicks)
+{
+ LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y);
+ if (cur_segment && cur_segment->handleScrollWheel(x, y, clicks))
+ {
+ return TRUE;
+ }
+
+ return LLUICtrl::handleScrollWheel(x, y, clicks);
+}
+
+BOOL LLTextBase::handleToolTip(S32 x, S32 y, MASK mask)
+{
+ LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y);
+ if (cur_segment && cur_segment->handleToolTip(x, y, mask))
+ {
+ return TRUE;
+ }
+
+ return LLUICtrl::handleToolTip(x, y, mask);
+}
+
+
+void LLTextBase::reshape(S32 width, S32 height, BOOL called_from_parent)
+{
+ if (width != getRect().getWidth() || height != getRect().getHeight())
+ {
+ bool scrolled_to_bottom = mScroller ? mScroller->isAtBottom() : false;
+
+ LLUICtrl::reshape( width, height, called_from_parent );
+
+ if (mScroller && scrolled_to_bottom && mTrackEnd)
+ {
+ // keep bottom of text buffer visible
+ // do this here as well as in reflow to handle case
+ // where shrinking from top, which causes buffer to temporarily
+ // not be scrolled to the bottom, since the scroll index
+ // specified the _top_ of the visible document region
+ mScroller->goToBottom();
+ }
+
+ // do this first after reshape, because other things depend on
+ // up-to-date mVisibleTextRect
+ updateRects();
+
+ needsReflow();
+ }
+}
+
+void LLTextBase::draw()
+{
+ // reflow if needed, on demand
+ reflow();
+
+ // then update scroll position, as cursor may have moved
+ if (!mReadOnly)
+ {
+ updateScrollFromCursor();
+ }
+
+ LLRect doc_rect;
+ if (mScroller)
+ {
+ mScroller->localRectToOtherView(mScroller->getContentWindowRect(), &doc_rect, this);
+ }
+ else
+ {
+ doc_rect = getLocalRect();
+ }
+
+ if (mBGVisible)
+ {
+ // clip background rect against extents, if we support scrolling
+ LLRect bg_rect = mVisibleTextRect;
+ if (mScroller)
+ {
+ bg_rect.intersectWith(doc_rect);
+ }
+ LLColor4 bg_color = mReadOnly
+ ? mReadOnlyBgColor.get()
+ : hasFocus()
+ ? mFocusBgColor.get()
+ : mWriteableBgColor.get();
+ gl_rect_2d(doc_rect, bg_color, TRUE);
+ }
+
+ // draw document view
+ LLUICtrl::draw();
+
+ {
+ // only clip if we support scrolling...
+ // since convention is that text boxes never vertically truncate their contents
+ // regardless of rect bounds
+ LLLocalClipRect clip(doc_rect, mScroller != NULL);
+ drawSelectionBackground();
+ drawText();
+ drawCursor();
+ }
+}
+
+
+//virtual
+void LLTextBase::setColor( const LLColor4& c )
+{
+ mFgColor = c;
+ mStyleDirty = true;
+}
+
+//virtual
+void LLTextBase::setReadOnlyColor(const LLColor4 &c)
+{
+ mReadOnlyFgColor = c;
+ mStyleDirty = true;
+}
+
+//virtual
+void LLTextBase::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;
+ mSelectionEnd = 0;
+ mIsSelecting = FALSE;
+}
+
+
+// Sets the scrollbar from the cursor position
+void LLTextBase::updateScrollFromCursor()
+{
+ // Update scroll position even in read-only mode (when there's no cursor displayed)
+ // because startOfDoc()/endOfDoc() modify cursor position. See EXT-736.
+
+ if (!mScrollNeeded || !mScroller)
+ {
+ return;
+ }
+ mScrollNeeded = FALSE;
+
+ // scroll so that the cursor is at the top of the page
+ LLRect scroller_doc_window = getVisibleDocumentRect();
+ LLRect cursor_rect_doc = getLocalRectFromDocIndex(mCursorPos);
+ cursor_rect_doc.translate(scroller_doc_window.mLeft, scroller_doc_window.mBottom);
+ mScroller->scrollToShowRect(cursor_rect_doc, LLRect(0, scroller_doc_window.getHeight() - 5, scroller_doc_window.getWidth(), 5));
+}
+
+S32 LLTextBase::getLeftOffset(S32 width)
+{
+ switch (mHAlign)
+ {
+ case LLFontGL::LEFT:
+ return mHPad;
+ case LLFontGL::HCENTER:
+ return mHPad + llmax(0, (mVisibleTextRect.getWidth() - width - mHPad) / 2);
+ case LLFontGL::RIGHT:
+ return mVisibleTextRect.getWidth() - width;
+ default:
+ return mHPad;
+ }
+}
+
+
+static LLFastTimer::DeclareTimer FTM_TEXT_REFLOW ("Text Reflow");
+void LLTextBase::reflow()
+{
+ LLFastTimer ft(FTM_TEXT_REFLOW);
+
+ updateSegments();
+
+ if (mReflowIndex == S32_MAX)
+ {
+ return;
+ }
+
+ bool scrolled_to_bottom = mScroller ? mScroller->isAtBottom() : false;
+
+ LLRect cursor_rect = getLocalRectFromDocIndex(mCursorPos);
+ bool follow_selection = getLocalRect().overlaps(cursor_rect); // cursor is (potentially) visible
+
+ // store in top-left relative coordinates to avoid issues with horizontal scrollbar appearing and disappearing
+ cursor_rect.mTop = mVisibleTextRect.mTop - cursor_rect.mTop;
+ cursor_rect.mBottom = mVisibleTextRect.mTop - cursor_rect.mBottom;
+
+ S32 first_line = getFirstVisibleLine();
+
+ // if scroll anchor not on first line, update it to first character of first line
+ if (!mLineInfoList.empty()
+ && (mScrollIndex < mLineInfoList[first_line].mDocIndexStart
+ || mScrollIndex >= mLineInfoList[first_line].mDocIndexEnd))
+ {
+ mScrollIndex = mLineInfoList[first_line].mDocIndexStart;
+ }
+ LLRect first_char_rect = getLocalRectFromDocIndex(mScrollIndex);
+ // store in top-left relative coordinates to avoid issues with horizontal scrollbar appearing and disappearing
+ first_char_rect.mTop = mVisibleTextRect.mTop - first_char_rect.mTop;
+ first_char_rect.mBottom = mVisibleTextRect.mTop - first_char_rect.mBottom;
+
+ S32 reflow_count = 0;
+ while(mReflowIndex < S32_MAX)
+ {
+ // we can get into an infinite loop if the document height does not monotonically increase
+ // with decreasing width (embedded ui elements with alternate layouts). In that case,
+ // we want to stop reflowing after 2 iterations. We use 2, since we need to handle the case
+ // of introducing a vertical scrollbar causing a reflow with less width. We should also always
+ // use an even number of iterations to avoid user visible oscillation of the layout
+ if(++reflow_count > 2)
+ {
+ lldebugs << "Breaking out of reflow due to possible infinite loop in " << getName() << llendl;
+ break;
+ }
+
+ S32 start_index = mReflowIndex;
+ mReflowIndex = S32_MAX;
+
+ // shrink document to minimum size (visible portion of text widget)
+ // to force inlined widgets with follows set to shrink
+ mDocumentView->reshape(mVisibleTextRect.getWidth(), mDocumentView->getRect().getHeight());
+
+ S32 cur_top = 0;
+
+ segment_set_t::iterator seg_iter = mSegments.begin();
+ S32 seg_offset = 0;
+ S32 line_start_index = 0;
+ const S32 text_available_width = mVisibleTextRect.getWidth() - mHPad; // reserve room for margin
+ S32 remaining_pixels = text_available_width;
+ S32 line_count = 0;
+
+ // find and erase line info structs starting at start_index and going to end of document
+ if (!mLineInfoList.empty())
+ {
+ // find first element whose end comes after start_index
+ line_list_t::iterator iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), start_index, line_end_compare());
+ line_start_index = iter->mDocIndexStart;
+ line_count = iter->mLineNum;
+ cur_top = iter->mRect.mTop;
+ getSegmentAndOffset(iter->mDocIndexStart, &seg_iter, &seg_offset);
+ mLineInfoList.erase(iter, mLineInfoList.end());
+ }
+
+ S32 line_height = 0;
+
+ while(seg_iter != mSegments.end())
+ {
+ LLTextSegmentPtr segment = *seg_iter;
+
+ // track maximum height of any segment on this line
+ S32 cur_index = segment->getStart() + seg_offset;
+
+ // ask segment how many character fit in remaining space
+ S32 character_count = segment->getNumChars(getWordWrap() ? llmax(0, remaining_pixels) : S32_MAX,
+ seg_offset,
+ cur_index - line_start_index,
+ S32_MAX);
+
+ S32 segment_width, segment_height;
+ bool force_newline = segment->getDimensions(seg_offset, character_count, segment_width, segment_height);
+ // grow line height as necessary based on reported height of this segment
+ line_height = llmax(line_height, segment_height);
+ remaining_pixels -= segment_width;
+
+ seg_offset += character_count;
+
+ S32 last_segment_char_on_line = segment->getStart() + seg_offset;
+
+ S32 text_actual_width = text_available_width - remaining_pixels;
+ S32 text_left = getLeftOffset(text_actual_width);
+ LLRect line_rect(text_left,
+ cur_top,
+ text_left + text_actual_width,
+ cur_top - line_height);
+
+ // if we didn't finish the current segment...
+ if (last_segment_char_on_line < segment->getEnd())
+ {
+ // add line info and keep going
+ mLineInfoList.push_back(line_info(
+ line_start_index,
+ last_segment_char_on_line,
+ line_rect,
+ line_count));
+
+ line_start_index = segment->getStart() + seg_offset;
+ cur_top -= llround((F32)line_height * mLineSpacingMult) + mLineSpacingPixels;
+ remaining_pixels = text_available_width;
+ line_height = 0;
+ }
+ // ...just consumed last segment..
+ else if (++segment_set_t::iterator(seg_iter) == mSegments.end())
+ {
+ mLineInfoList.push_back(line_info(
+ line_start_index,
+ last_segment_char_on_line,
+ line_rect,
+ line_count));
+ cur_top -= llround((F32)line_height * mLineSpacingMult) + mLineSpacingPixels;
+ break;
+ }
+ // ...or finished a segment and there are segments remaining on this line
+ else
+ {
+ // subtract pixels used and increment segment
+ if (force_newline)
+ {
+ mLineInfoList.push_back(line_info(
+ line_start_index,
+ last_segment_char_on_line,
+ line_rect,
+ line_count));
+ line_start_index = segment->getStart() + seg_offset;
+ cur_top -= llround((F32)line_height * mLineSpacingMult) + mLineSpacingPixels;
+ line_height = 0;
+ remaining_pixels = text_available_width;
+ }
+ ++seg_iter;
+ seg_offset = 0;
+ }
+ if (force_newline)
+ {
+ line_count++;
+ }
+ }
+
+ // calculate visible region for diplaying text
+ updateRects();
+
+ for (segment_set_t::iterator segment_it = mSegments.begin();
+ segment_it != mSegments.end();
+ ++segment_it)
+ {
+ LLTextSegmentPtr segmentp = *segment_it;
+ segmentp->updateLayout(*this);
+
+ }
+ }
+
+ // apply scroll constraints after reflowing text
+ if (!hasMouseCapture() && mScroller)
+ {
+ if (scrolled_to_bottom && mTrackEnd)
+ {
+ // keep bottom of text buffer visible
+ endOfDoc();
+ }
+ else if (hasSelection() && follow_selection)
+ {
+ // keep cursor in same vertical position on screen when selecting text
+ LLRect new_cursor_rect_doc = getDocRectFromDocIndex(mCursorPos);
+ LLRect old_cursor_rect = cursor_rect;
+ old_cursor_rect.mTop = mVisibleTextRect.mTop - cursor_rect.mTop;
+ old_cursor_rect.mBottom = mVisibleTextRect.mTop - cursor_rect.mBottom;
+
+ mScroller->scrollToShowRect(new_cursor_rect_doc, old_cursor_rect);
+ }
+ else
+ {
+ // keep first line of text visible
+ LLRect new_first_char_rect = getDocRectFromDocIndex(mScrollIndex);
+
+ // pass in desired rect in the coordinate frame of the document viewport
+ LLRect old_first_char_rect = first_char_rect;
+ old_first_char_rect.mTop = mVisibleTextRect.mTop - first_char_rect.mTop;
+ old_first_char_rect.mBottom = mVisibleTextRect.mTop - first_char_rect.mBottom;
+
+ mScroller->scrollToShowRect(new_first_char_rect, old_first_char_rect);
+ }
+ }
+
+ // reset desired x cursor position
+ updateCursorXPos();
+}
+
+LLRect LLTextBase::getTextBoundingRect()
+{
+ reflow();
+ return mTextBoundingRect;
+}
+
+
+void LLTextBase::clearSegments()
+{
+ mSegments.clear();
+ createDefaultSegment();
+}
+
+S32 LLTextBase::getLineStart( S32 line ) const
+{
+ S32 num_lines = getLineCount();
+ if (num_lines == 0)
+ {
+ return 0;
+ }
+
+ line = llclamp(line, 0, num_lines-1);
+ return mLineInfoList[line].mDocIndexStart;
+}
+
+S32 LLTextBase::getLineEnd( S32 line ) const
+{
+ S32 num_lines = getLineCount();
+ if (num_lines == 0)
+ {
+ return 0;
+ }
+
+ line = llclamp(line, 0, num_lines-1);
+ return mLineInfoList[line].mDocIndexEnd;
+}
+
+
+
+S32 LLTextBase::getLineNumFromDocIndex( S32 doc_index, bool include_wordwrap) const
+{
+ if (mLineInfoList.empty())
+ {
+ return 0;
+ }
+ else
+ {
+ line_list_t::const_iterator iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), doc_index, line_end_compare());
+ if (include_wordwrap)
+ {
+ return iter - mLineInfoList.begin();
+ }
+ else
+ {
+ if (iter == mLineInfoList.end())
+ {
+ return mLineInfoList.back().mLineNum;
+ }
+ else
+ {
+ return iter->mLineNum;
+ }
+ }
+ }
+}
+
+// Given an offset into text (pos), find the corresponding line (from the start of the doc) and an offset into the line.
+S32 LLTextBase::getLineOffsetFromDocIndex( S32 startpos, bool include_wordwrap) const
+{
+ if (mLineInfoList.empty())
+ {
+ return startpos;
+ }
+ else
+ {
+ line_list_t::const_iterator iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), startpos, line_end_compare());
+ return startpos - iter->mDocIndexStart;
+ }
+}
+
+S32 LLTextBase::getFirstVisibleLine() const
+{
+ LLRect visible_region = getVisibleDocumentRect();
+
+ // binary search for line that starts before top of visible buffer
+ line_list_t::const_iterator iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), visible_region.mTop, compare_bottom());
+
+ return iter - mLineInfoList.begin();
+}
+
+std::pair<S32, S32> LLTextBase::getVisibleLines(bool fully_visible)
+{
+ LLRect visible_region = getVisibleDocumentRect();
+ line_list_t::const_iterator first_iter;
+ line_list_t::const_iterator last_iter;
+
+ // make sure we have an up-to-date mLineInfoList
+ reflow();
+
+ if (fully_visible)
+ {
+ first_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), visible_region.mTop, compare_top());
+ last_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), visible_region.mBottom, compare_bottom());
+ }
+ else
+ {
+ first_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), visible_region.mTop, compare_bottom());
+ last_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), visible_region.mBottom, compare_top());
+ }
+ return std::pair<S32, S32>(first_iter - mLineInfoList.begin(), last_iter - mLineInfoList.begin());
+}
+
+
+
+LLTextViewModel* LLTextBase::getViewModel() const
+{
+ return (LLTextViewModel*)mViewModel.get();
+}
+
+void LLTextBase::addDocumentChild(LLView* view)
+{
+ mDocumentView->addChild(view);
+}
+
+void LLTextBase::removeDocumentChild(LLView* view)
+{
+ mDocumentView->removeChild(view);
+}
+
+
+static LLFastTimer::DeclareTimer FTM_UPDATE_TEXT_SEGMENTS("Update Text Segments");
+void LLTextBase::updateSegments()
+{
+ LLFastTimer ft(FTM_UPDATE_TEXT_SEGMENTS);
+ createDefaultSegment();
+}
+
+void LLTextBase::getSegmentAndOffset( S32 startpos, segment_set_t::const_iterator* seg_iter, S32* offsetp ) const
+{
+ *seg_iter = getSegIterContaining(startpos);
+ if (*seg_iter == mSegments.end())
+ {
+ *offsetp = 0;
+ }
+ else
+ {
+ *offsetp = startpos - (**seg_iter)->getStart();
+ }
+}
+
+void LLTextBase::getSegmentAndOffset( S32 startpos, segment_set_t::iterator* seg_iter, S32* offsetp )
+{
+ *seg_iter = getSegIterContaining(startpos);
+ if (*seg_iter == mSegments.end())
+ {
+ *offsetp = 0;
+ }
+ else
+ {
+ *offsetp = startpos - (**seg_iter)->getStart();
+ }
+}
+
+LLTextBase::segment_set_t::iterator LLTextBase::getSegIterContaining(S32 index)
+{
+ static LLPointer<LLIndexSegment> index_segment = new LLIndexSegment();
+
+ if (index > getLength()) { return mSegments.end(); }
+
+ // when there are no segments, we return the end iterator, which must be checked by caller
+ if (mSegments.size() <= 1) { return mSegments.begin(); }
+
+ //FIXME: avoid operator new somehow (without running into refcount problems)
+ index_segment->setStart(index);
+ index_segment->setEnd(index);
+ segment_set_t::iterator it = mSegments.upper_bound(index_segment);
+ return it;
+}
+
+LLTextBase::segment_set_t::const_iterator LLTextBase::getSegIterContaining(S32 index) const
+{
+ static LLPointer<LLIndexSegment> index_segment = new LLIndexSegment();
+
+ if (index > getLength()) { return mSegments.end(); }
+
+ // when there are no segments, we return the end iterator, which must be checked by caller
+ if (mSegments.size() <= 1) { return mSegments.begin(); }
+
+ index_segment->setStart(index);
+ index_segment->setEnd(index);
+ LLTextBase::segment_set_t::const_iterator it = mSegments.upper_bound(index_segment);
+ return it;
+}
+
+// Finds the text segment (if any) at the give local screen position
+LLTextSegmentPtr LLTextBase::getSegmentAtLocalPos( S32 x, S32 y, bool hit_past_end_of_line)
+{
+ // Find the cursor position at the requested local screen position
+ S32 offset = getDocIndexFromLocalCoord( x, y, FALSE, hit_past_end_of_line);
+ segment_set_t::iterator seg_iter = getSegIterContaining(offset);
+ if (seg_iter != mSegments.end())
+ {
+ return *seg_iter;
+ }
+ else
+ {
+ return LLTextSegmentPtr();
+ }
+}
+
+void LLTextBase::createUrlContextMenu(S32 x, S32 y, const std::string &in_url)
+{
+ // work out the XUI menu file to use for this url
+ LLUrlMatch match;
+ std::string url = in_url;
+ if (! LLUrlRegistry::instance().findUrl(url, match))
+ {
+ return;
+ }
+
+ std::string xui_file = match.getMenuName();
+ if (xui_file.empty())
+ {
+ return;
+ }
+
+ // set up the callbacks for all of the potential menu items, N.B. we
+ // don't use const ref strings in callbacks in case url goes out of scope
+ LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
+ registrar.add("Url.Open", boost::bind(&LLUrlAction::openURL, url));
+ registrar.add("Url.OpenInternal", boost::bind(&LLUrlAction::openURLInternal, url));
+ registrar.add("Url.OpenExternal", boost::bind(&LLUrlAction::openURLExternal, url));
+ registrar.add("Url.Execute", boost::bind(&LLUrlAction::executeSLURL, url));
+ 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));
+
+ // create and return the context menu from the XUI file
+ delete mPopupMenu;
+ mPopupMenu = LLUICtrlFactory::getInstance()->createFromFile<LLContextMenu>(xui_file, LLMenuGL::sMenuContainer,
+ LLMenuHolderGL::child_registry_t::instance());
+ if (mPopupMenu)
+ {
+ mPopupMenu->show(x, y);
+ LLMenuGL::showPopup(this, mPopupMenu, x, y);
+ }
+}
+
+void LLTextBase::setText(const LLStringExplicit &utf8str, const LLStyle::Params& input_params)
+{
+ // clear out the existing text and segments
+ getViewModel()->setDisplay(LLWStringUtil::null);
+
+ clearSegments();
+// createDefaultSegment();
+
+ deselect();
+
+ // append the new text (supports Url linking)
+ std::string text(utf8str);
+ LLStringUtil::removeCRLF(text);
+
+ // appendText modifies mCursorPos...
+ appendText(text, false, input_params);
+ // ...so move cursor to top after appending text
+ startOfDoc();
+
+ onValueChange(0, getLength());
+}
+
+//virtual
+std::string LLTextBase::getText() const
+{
+ return getViewModel()->getValue().asString();
+}
+
+void LLTextBase::appendTextImpl(const std::string &new_text, const LLStyle::Params& input_params)
+{
+ LLStyle::Params style_params(input_params);
+ style_params.fillFrom(getDefaultStyleParams());
+
+ S32 part = (S32)LLTextParser::WHOLE;
+ if(mParseHTML)
+ {
+ S32 start=0,end=0;
+ LLUrlMatch match;
+ std::string text = new_text;
+ while ( LLUrlRegistry::instance().findUrl(text, match,
+ boost::bind(&LLTextBase::replaceUrlLabel, this, _1, _2)) )
+ {
+ start = match.getStart();
+ end = match.getEnd()+1;
+
+ LLStyle::Params link_params = style_params;
+ link_params.color = match.getColor();
+ link_params.readonly_color = match.getColor();
+ link_params.font.style("UNDERLINE");
+ link_params.link_href = match.getUrl();
+
+ // output the text before the Url
+ if (start > 0)
+ {
+ if (part == (S32)LLTextParser::WHOLE ||
+ part == (S32)LLTextParser::START)
+ {
+ part = (S32)LLTextParser::START;
+ }
+ else
+ {
+ part = (S32)LLTextParser::MIDDLE;
+ }
+ std::string subtext=text.substr(0,start);
+ appendAndHighlightText(subtext, part, style_params);
+ }
+
+ // inserts an avatar icon preceding the Url if appropriate
+ LLTextUtil::processUrlMatch(&match,this);
+
+ // output the styled Url (unless we've been asked to suppress hyperlinking)
+ if (match.isLinkDisabled())
+ {
+ appendAndHighlightText(match.getLabel(), part, style_params);
+ }
+ else
+ {
+ appendAndHighlightText(match.getLabel(), part, link_params, match.underlineOnHoverOnly());
+
+ // set the tooltip for the Url label
+ if (! match.getTooltip().empty())
+ {
+ segment_set_t::iterator it = getSegIterContaining(getLength()-1);
+ if (it != mSegments.end())
+ {
+ LLTextSegmentPtr segment = *it;
+ segment->setToolTip(match.getTooltip());
+ }
+ }
+ }
+ // move on to the rest of the text after the Url
+ if (end < (S32)text.length())
+ {
+ text = text.substr(end,text.length() - end);
+ end=0;
+ part=(S32)LLTextParser::END;
+ }
+ else
+ {
+ break;
+ }
+ }
+ if (part != (S32)LLTextParser::WHOLE)
+ part=(S32)LLTextParser::END;
+ if (end < (S32)text.length())
+ appendAndHighlightText(text, part, style_params);
+ }
+ else
+ {
+ appendAndHighlightText(new_text, part, style_params);
+ }
+}
+
+void LLTextBase::appendText(const std::string &new_text, bool prepend_newline, const LLStyle::Params& input_params)
+{
+ if (new_text.empty())
+ return;
+
+ if(prepend_newline)
+ appendLineBreakSegment(input_params);
+ appendTextImpl(new_text,input_params);
+}
+
+void LLTextBase::needsReflow(S32 index)
+{
+ lldebugs << "reflow on object " << (void*)this << " index = " << mReflowIndex << ", new index = " << index << llendl;
+ mReflowIndex = llmin(mReflowIndex, index);
+}
+
+void LLTextBase::appendLineBreakSegment(const LLStyle::Params& style_params)
+{
+ segment_vec_t segments;
+ LLStyleConstSP sp(new LLStyle(style_params));
+ segments.push_back(new LLLineBreakTextSegment(sp, getLength()));
+
+ insertStringNoUndo(getLength(), utf8str_to_wstring("\n"), &segments);
+}
+
+void LLTextBase::appendImageSegment(const LLStyle::Params& style_params)
+{
+ if(getPlainText())
+ {
+ return;
+ }
+ segment_vec_t segments;
+ LLStyleConstSP sp(new LLStyle(style_params));
+ segments.push_back(new LLImageTextSegment(sp, getLength(),*this));
+
+ insertStringNoUndo(getLength(), utf8str_to_wstring(" "), &segments);
+}
+
+void LLTextBase::appendWidget(const LLInlineViewSegment::Params& params, const std::string& text, bool allow_undo)
+{
+ segment_vec_t segments;
+ LLWString widget_wide_text = utf8str_to_wstring(text);
+ segments.push_back(new LLInlineViewSegment(params, getLength(), getLength() + widget_wide_text.size()));
+
+ insertStringNoUndo(getLength(), widget_wide_text, &segments);
+}
+
+void LLTextBase::appendAndHighlightTextImpl(const std::string &new_text, S32 highlight_part, const LLStyle::Params& style_params, bool underline_on_hover_only)
+{
+ // Save old state
+ S32 selection_start = mSelectionStart;
+ S32 selection_end = mSelectionEnd;
+ BOOL was_selecting = mIsSelecting;
+ S32 cursor_pos = mCursorPos;
+ S32 old_length = getLength();
+ BOOL cursor_was_at_end = (mCursorPos == old_length);
+
+ deselect();
+
+ setCursorPos(old_length);
+
+ if (mParseHighlights)
+ {
+ LLStyle::Params highlight_params(style_params);
+
+ LLSD pieces = LLTextParser::instance().parsePartialLineHighlights(new_text, highlight_params.color(), (LLTextParser::EHighlightPosition)highlight_part);
+ for (S32 i = 0; i < pieces.size(); i++)
+ {
+ LLSD color_llsd = pieces[i]["color"];
+ LLColor4 lcolor;
+ lcolor.setValue(color_llsd);
+ highlight_params.color = lcolor;
+
+ LLWString wide_text;
+ wide_text = utf8str_to_wstring(pieces[i]["text"].asString());
+
+ S32 cur_length = getLength();
+ LLStyleConstSP sp(new LLStyle(highlight_params));
+ LLTextSegmentPtr segmentp;
+ if(underline_on_hover_only)
+ {
+ highlight_params.font.style("NORMAL");
+ LLStyleConstSP normal_sp(new LLStyle(highlight_params));
+ segmentp = new LLOnHoverChangeableTextSegment(sp, normal_sp, cur_length, cur_length + wide_text.size(), *this);
+ }
+ else
+ {
+ segmentp = new LLNormalTextSegment(sp, cur_length, cur_length + wide_text.size(), *this);
+ }
+ segment_vec_t segments;
+ segments.push_back(segmentp);
+ insertStringNoUndo(cur_length, wide_text, &segments);
+ }
+ }
+ else
+ {
+ LLWString wide_text;
+ wide_text = utf8str_to_wstring(new_text);
+
+ segment_vec_t segments;
+ S32 segment_start = old_length;
+ S32 segment_end = old_length + wide_text.size();
+ LLStyleConstSP sp(new LLStyle(style_params));
+ if (underline_on_hover_only)
+ {
+ LLStyle::Params normal_style_params(style_params);
+ normal_style_params.font.style("NORMAL");
+ LLStyleConstSP normal_sp(new LLStyle(normal_style_params));
+ segments.push_back(new LLOnHoverChangeableTextSegment(sp, normal_sp, segment_start, segment_end, *this ));
+ }
+ else
+ {
+ segments.push_back(new LLNormalTextSegment(sp, segment_start, segment_end, *this ));
+ }
+
+ insertStringNoUndo(getLength(), wide_text, &segments);
+ }
+
+ // Set the cursor and scroll position
+ if( selection_start != selection_end )
+ {
+ mSelectionStart = selection_start;
+ mSelectionEnd = selection_end;
+
+ mIsSelecting = was_selecting;
+ setCursorPos(cursor_pos);
+ }
+ else if( cursor_was_at_end )
+ {
+ setCursorPos(getLength());
+ }
+ else
+ {
+ setCursorPos(cursor_pos);
+ }
+}
+
+void LLTextBase::appendAndHighlightText(const std::string &new_text, S32 highlight_part, const LLStyle::Params& style_params, bool underline_on_hover_only)
+{
+ if (new_text.empty()) return;
+
+ std::string::size_type start = 0;
+ std::string::size_type pos = new_text.find("\n",start);
+
+ while(pos!=-1)
+ {
+ if(pos!=start)
+ {
+ std::string str = std::string(new_text,start,pos-start);
+ appendAndHighlightTextImpl(str,highlight_part, style_params, underline_on_hover_only);
+ }
+ appendLineBreakSegment(style_params);
+ start = pos+1;
+ pos = new_text.find("\n",start);
+ }
+
+ std::string str = std::string(new_text,start,new_text.length()-start);
+ appendAndHighlightTextImpl(str,highlight_part, style_params, underline_on_hover_only);
+}
+
+
+void LLTextBase::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;
+ LLStyleConstSP style = seg->getStyle();
+
+ // update segment start/end length in case we replaced text earlier
+ S32 seg_length = seg->getEnd() - seg->getStart();
+ seg->setStart(seg_start);
+ seg->setEnd(seg_start + seg_length);
+
+ // if we find a link with our Url, then replace the label
+ if (style->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 LLTextBase::setWText(const LLWString& text)
+{
+ setText(wstring_to_utf8str(text));
+}
+
+const LLWString& LLTextBase::getWText() const
+{
+ return getViewModel()->getDisplay();
+}
+
+// If round is true, if the position is on the right half of a character, the cursor
+// will be put to its right. If round is false, the cursor will always be put to the
+// character's left.
+
+S32 LLTextBase::getDocIndexFromLocalCoord( S32 local_x, S32 local_y, BOOL round, bool hit_past_end_of_line) const
+{
+ // Figure out which line we're nearest to.
+ LLRect visible_region = getVisibleDocumentRect();
+
+ // binary search for line that starts before local_y
+ line_list_t::const_iterator line_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), local_y - mVisibleTextRect.mBottom + visible_region.mBottom, compare_bottom());
+
+ if (line_iter == mLineInfoList.end())
+ {
+ return getLength(); // past the end
+ }
+
+ S32 pos = getLength();
+ S32 start_x = mVisibleTextRect.mLeft + line_iter->mRect.mLeft - visible_region.mLeft;
+
+ segment_set_t::iterator line_seg_iter;
+ S32 line_seg_offset;
+ for(getSegmentAndOffset(line_iter->mDocIndexStart, &line_seg_iter, &line_seg_offset);
+ line_seg_iter != mSegments.end();
+ ++line_seg_iter, line_seg_offset = 0)
+ {
+ const LLTextSegmentPtr segmentp = *line_seg_iter;
+
+ S32 segment_line_start = segmentp->getStart() + line_seg_offset;
+ S32 segment_line_length = llmin(segmentp->getEnd(), line_iter->mDocIndexEnd) - segment_line_start;
+ S32 text_width, text_height;
+ bool newline = segmentp->getDimensions(line_seg_offset, segment_line_length, text_width, text_height);
+
+ if(newline)
+ {
+ pos = segment_line_start + segmentp->getOffset(local_x - start_x, line_seg_offset, segment_line_length, round);
+ break;
+ }
+
+ // if we've reached a line of text *below* the mouse cursor, doc index is first character on that line
+ if (hit_past_end_of_line && 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
+ {
+ // Figure out which character we're nearest to.
+ S32 offset;
+ if (!segmentp->canEdit())
+ {
+ S32 segment_width, segment_height;
+ segmentp->getDimensions(0, segmentp->getEnd() - segmentp->getStart(), segment_width, segment_height);
+ if (round && local_x - start_x > segment_width / 2)
+ {
+ offset = segment_line_length;
+ }
+ else
+ {
+ offset = 0;
+ }
+ }
+ else
+ {
+ offset = segmentp->getOffset(local_x - start_x, line_seg_offset, segment_line_length, round);
+ }
+ pos = segment_line_start + offset;
+ break;
+ }
+ else if (hit_past_end_of_line && segmentp->getEnd() > line_iter->mDocIndexEnd - 1)
+ {
+ // segment wraps to next line, so just set doc pos to the end of the line
+ // 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;
+ }
+
+ return pos;
+}
+
+// returns rectangle of insertion caret
+// in document coordinate frame from given index into text
+LLRect LLTextBase::getDocRectFromDocIndex(S32 pos) const
+{
+ if (mLineInfoList.empty())
+ {
+ return LLRect();
+ }
+
+ LLRect doc_rect;
+
+ // clamp pos to valid values
+ pos = llclamp(pos, 0, mLineInfoList.back().mDocIndexEnd - 1);
+
+ // find line that contains cursor
+ line_list_t::const_iterator line_iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), pos, line_end_compare());
+
+ doc_rect.mLeft = line_iter->mRect.mLeft;
+ doc_rect.mBottom = line_iter->mRect.mBottom;
+ doc_rect.mTop = line_iter->mRect.mTop;
+
+ segment_set_t::iterator line_seg_iter;
+ S32 line_seg_offset;
+ segment_set_t::iterator cursor_seg_iter;
+ S32 cursor_seg_offset;
+ getSegmentAndOffset(line_iter->mDocIndexStart, &line_seg_iter, &line_seg_offset);
+ getSegmentAndOffset(pos, &cursor_seg_iter, &cursor_seg_offset);
+
+ while(line_seg_iter != mSegments.end())
+ {
+ const LLTextSegmentPtr segmentp = *line_seg_iter;
+
+ if (line_seg_iter == cursor_seg_iter)
+ {
+ // cursor advanced to right based on difference in offset of cursor to start of line
+ S32 segment_width, segment_height;
+ segmentp->getDimensions(line_seg_offset, cursor_seg_offset - line_seg_offset, segment_width, segment_height);
+ doc_rect.mLeft += segment_width;
+
+ break;
+ }
+ else
+ {
+ // add remainder of current text segment to cursor position
+ S32 segment_width, segment_height;
+ segmentp->getDimensions(line_seg_offset, (segmentp->getEnd() - segmentp->getStart()) - line_seg_offset, segment_width, segment_height);
+ doc_rect.mLeft += segment_width;
+ // offset will be 0 for all segments after the first
+ line_seg_offset = 0;
+ // go to next text segment on this line
+ ++line_seg_iter;
+ }
+ }
+
+ // set rect to 0 width
+ doc_rect.mRight = doc_rect.mLeft;
+
+ return doc_rect;
+}
+
+LLRect LLTextBase::getLocalRectFromDocIndex(S32 pos) const
+{
+ LLRect content_window_rect = mScroller ? mScroller->getContentWindowRect() : getLocalRect();
+ if (mBorderVisible)
+ {
+ content_window_rect.stretch(-1);
+ }
+
+ LLRect local_rect;
+
+ if (mLineInfoList.empty())
+ {
+ // return default height rect in upper left
+ local_rect = content_window_rect;
+ local_rect.mBottom = local_rect.mTop - (S32)(mDefaultFont->getLineHeight());
+ return local_rect;
+ }
+
+ // get the rect in document coordinates
+ LLRect doc_rect = getDocRectFromDocIndex(pos);
+
+ // compensate for scrolled, inset view of doc
+ LLRect scrolled_view_rect = getVisibleDocumentRect();
+ local_rect = doc_rect;
+ local_rect.translate(content_window_rect.mLeft - scrolled_view_rect.mLeft,
+ content_window_rect.mBottom - scrolled_view_rect.mBottom);
+
+ return local_rect;
+}
+
+void LLTextBase::updateCursorXPos()
+{
+ // reset desired x cursor position
+ mDesiredXPixel = getLocalRectFromDocIndex(mCursorPos).mLeft;
+}
+
+
+void LLTextBase::startOfLine()
+{
+ S32 offset = getLineOffsetFromDocIndex(mCursorPos);
+ setCursorPos(mCursorPos - offset);
+}
+
+void LLTextBase::endOfLine()
+{
+ S32 line = getLineNumFromDocIndex(mCursorPos);
+ S32 num_lines = getLineCount();
+ if (line + 1 >= num_lines)
+ {
+ setCursorPos(getLength());
+ }
+ else
+ {
+ setCursorPos( getLineStart(line + 1) - 1 );
+ }
+}
+
+void LLTextBase::startOfDoc()
+{
+ setCursorPos(0);
+ if (mScroller)
+ {
+ mScroller->goToTop();
+ }
+}
+
+void LLTextBase::endOfDoc()
+{
+ setCursorPos(getLength());
+ if (mScroller)
+ {
+ mScroller->goToBottom();
+ }
+}
+
+void LLTextBase::changePage( S32 delta )
+{
+ const S32 PIXEL_OVERLAP_ON_PAGE_CHANGE = 10;
+ if (delta == 0 || !mScroller) return;
+
+ LLRect cursor_rect = getLocalRectFromDocIndex(mCursorPos);
+
+ if( delta == -1 )
+ {
+ mScroller->pageUp(PIXEL_OVERLAP_ON_PAGE_CHANGE);
+ }
+ else
+ if( delta == 1 )
+ {
+ mScroller->pageDown(PIXEL_OVERLAP_ON_PAGE_CHANGE);
+ }
+
+ if (getLocalRectFromDocIndex(mCursorPos) == cursor_rect)
+ {
+ // cursor didn't change apparent position, so move to top or bottom of document, respectively
+ if (delta < 0)
+ {
+ startOfDoc();
+ }
+ else
+ {
+ endOfDoc();
+ }
+ }
+ else
+ {
+ setCursorAtLocalPos(cursor_rect.getCenterX(), cursor_rect.getCenterY(), true, false);
+ }
+}
+
+// Picks a new cursor position based on the screen size of text being drawn.
+void LLTextBase::setCursorAtLocalPos( S32 local_x, S32 local_y, bool round, bool keep_cursor_offset )
+{
+ setCursorPos(getDocIndexFromLocalCoord(local_x, local_y, round), keep_cursor_offset);
+}
+
+
+void LLTextBase::changeLine( S32 delta )
+{
+ S32 line = getLineNumFromDocIndex(mCursorPos);
+
+ S32 new_line = line;
+ if( (delta < 0) && (line > 0 ) )
+ {
+ new_line = line - 1;
+ }
+ else if( (delta > 0) && (line < (getLineCount() - 1)) )
+ {
+ new_line = line + 1;
+ }
+
+ LLRect visible_region = getVisibleDocumentRect();
+
+ S32 new_cursor_pos = getDocIndexFromLocalCoord(mDesiredXPixel, mLineInfoList[new_line].mRect.mBottom + mVisibleTextRect.mBottom - visible_region.mBottom, TRUE);
+ setCursorPos(new_cursor_pos, true);
+}
+
+bool LLTextBase::scrolledToStart()
+{
+ return mScroller->isAtTop();
+}
+
+bool LLTextBase::scrolledToEnd()
+{
+ return mScroller->isAtBottom();
+}
+
+
+bool LLTextBase::setCursor(S32 row, S32 column)
+{
+ if (0 <= row && row < (S32)mLineInfoList.size())
+ {
+ S32 doc_pos = mLineInfoList[row].mDocIndexStart;
+ column = llclamp(column, 0, mLineInfoList[row].mDocIndexEnd - mLineInfoList[row].mDocIndexStart - 1);
+ doc_pos += column;
+ updateCursorXPos();
+
+ return setCursorPos(doc_pos);
+ }
+ return false;
+}
+
+
+bool LLTextBase::setCursorPos(S32 cursor_pos, bool keep_cursor_offset)
+{
+ S32 new_cursor_pos = cursor_pos;
+ if (new_cursor_pos != mCursorPos)
+ {
+ new_cursor_pos = getEditableIndex(new_cursor_pos, new_cursor_pos >= mCursorPos);
+ }
+
+ mCursorPos = llclamp(new_cursor_pos, 0, (S32)getLength());
+ needsScroll();
+ if (!keep_cursor_offset)
+ updateCursorXPos();
+ // did we get requested position?
+ return new_cursor_pos == cursor_pos;
+}
+
+// constraint cursor to editable segments of document
+S32 LLTextBase::getEditableIndex(S32 index, bool increasing_direction)
+{
+ segment_set_t::iterator segment_iter;
+ S32 offset;
+ getSegmentAndOffset(index, &segment_iter, &offset);
+ if (segment_iter == mSegments.end())
+ {
+ return 0;
+ }
+
+ LLTextSegmentPtr segmentp = *segment_iter;
+
+ if (segmentp->canEdit())
+ {
+ return segmentp->getStart() + offset;
+ }
+ else if (segmentp->getStart() < index && index < segmentp->getEnd())
+ {
+ // bias towards document end
+ if (increasing_direction)
+ {
+ return segmentp->getEnd();
+ }
+ // bias towards document start
+ else
+ {
+ return segmentp->getStart();
+ }
+ }
+ else
+ {
+ return index;
+ }
+}
+
+void LLTextBase::updateRects()
+{
+ if (mLineInfoList.empty())
+ {
+ mTextBoundingRect = LLRect(0, mVPad, mHPad, 0);
+ }
+ else
+ {
+ mTextBoundingRect = mLineInfoList.begin()->mRect;
+ for (line_list_t::const_iterator line_iter = ++mLineInfoList.begin();
+ line_iter != mLineInfoList.end();
+ ++line_iter)
+ {
+ mTextBoundingRect.unionWith(line_iter->mRect);
+ }
+
+ mTextBoundingRect.mTop += mVPad;
+ // subtract a pixel off the bottom to deal with rounding errors in measuring font height
+ mTextBoundingRect.mBottom -= 1;
+
+ S32 delta_pos = -mTextBoundingRect.mBottom;
+ // move line segments to fit new document rect
+ for (line_list_t::iterator it = mLineInfoList.begin(); it != mLineInfoList.end(); ++it)
+ {
+ it->mRect.translate(0, delta_pos);
+ }
+ mTextBoundingRect.translate(0, delta_pos);
+ }
+
+ // update document container dimensions according to text contents
+ LLRect doc_rect = mTextBoundingRect;
+ // use old mVisibleTextRect constraint document to width of viewable region
+ doc_rect.mLeft = 0;
+
+ // allow horizontal scrolling?
+ // if so, use entire width of text contents
+ // otherwise, stop at width of mVisibleTextRect
+ //FIXME: consider use of getWordWrap() instead
+ doc_rect.mRight = mScroller
+ ? llmax(mVisibleTextRect.getWidth(), mTextBoundingRect.mRight)
+ : mVisibleTextRect.getWidth();
+
+ if (!mScroller)
+ {
+ // push doc rect to top of text widget
+ doc_rect.translate(0, mVisibleTextRect.getHeight() - doc_rect.mTop);
+ }
+
+ mDocumentView->setShape(doc_rect);
+
+ //update mVisibleTextRect *after* mDocumentView has been resized
+ // so that scrollbars are added if document needs to scroll
+ // since mVisibleTextRect does not include scrollbars
+ LLRect old_text_rect = mVisibleTextRect;
+ mVisibleTextRect = mScroller ? mScroller->getContentWindowRect() : getLocalRect();
+ //FIXME: replace border with image?
+ if (mBorderVisible)
+ {
+ mVisibleTextRect.stretch(-1);
+ }
+ if (mVisibleTextRect != old_text_rect)
+ {
+ needsReflow();
+ }
+
+ // update document container again, using new mVisibleTextRect (that has scrollbars enabled as needed)
+ doc_rect.mRight = mScroller
+ ? llmax(mVisibleTextRect.getWidth(), mTextBoundingRect.mRight)
+ : mVisibleTextRect.getWidth();
+ mDocumentView->setShape(doc_rect);
+}
+
+
+void LLTextBase::startSelection()
+{
+ if( !mIsSelecting )
+ {
+ mIsSelecting = TRUE;
+ mSelectionStart = mCursorPos;
+ mSelectionEnd = mCursorPos;
+ }
+}
+
+void LLTextBase::endSelection()
+{
+ if( mIsSelecting )
+ {
+ mIsSelecting = FALSE;
+ mSelectionEnd = mCursorPos;
+ }
+}
+
+// get portion of document that is visible in text editor
+LLRect LLTextBase::getVisibleDocumentRect() const
+{
+ if (mScroller)
+ {
+ return mScroller->getVisibleContentRect();
+ }
+ else
+ {
+ // entire document rect is visible when not scrolling
+ // but offset according to height of widget
+ LLRect doc_rect = mDocumentView->getLocalRect();
+ doc_rect.mLeft -= mDocumentView->getRect().mLeft;
+ // adjust for height of text above widget baseline
+ doc_rect.mBottom = doc_rect.getHeight() - mVisibleTextRect.getHeight();
+ return doc_rect;
+ }
+}
+
+boost::signals2::connection LLTextBase::setURLClickedCallback(const commit_signal_t::slot_type& cb)
+{
+ if (!mURLClickSignal)
+ {
+ mURLClickSignal = new commit_signal_t();
+ }
+ return mURLClickSignal->connect(cb);
+}
+
+//
+// LLTextSegment
+//
+
+LLTextSegment::~LLTextSegment()
+{}
+
+bool LLTextSegment::getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height) const { width = 0; height = 0; return false;}
+S32 LLTextSegment::getOffset(S32 segment_local_x_coord, S32 start_offset, S32 num_chars, bool round) const { return 0; }
+S32 LLTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars) const { return 0; }
+void LLTextSegment::updateLayout(const LLTextBase& editor) {}
+F32 LLTextSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRect& draw_rect) { return draw_rect.mLeft; }
+bool LLTextSegment::canEdit() const { return false; }
+void LLTextSegment::unlinkFromDocument(LLTextBase*) {}
+void LLTextSegment::linkToDocument(LLTextBase*) {}
+const LLColor4& LLTextSegment::getColor() const { return LLColor4::white; }
+//void LLTextSegment::setColor(const LLColor4 &color) {}
+LLStyleConstSP LLTextSegment::getStyle() const {static LLStyleConstSP sp(new LLStyle()); return sp; }
+void LLTextSegment::setStyle(LLStyleConstSP style) {}
+void LLTextSegment::setToken( LLKeywordToken* token ) {}
+LLKeywordToken* LLTextSegment::getToken() const { return NULL; }
+void LLTextSegment::setToolTip( const std::string &msg ) {}
+void LLTextSegment::dump() const {}
+BOOL LLTextSegment::handleMouseDown(S32 x, S32 y, MASK mask) { return FALSE; }
+BOOL LLTextSegment::handleMouseUp(S32 x, S32 y, MASK mask) { return FALSE; }
+BOOL LLTextSegment::handleMiddleMouseDown(S32 x, S32 y, MASK mask) { return FALSE; }
+BOOL LLTextSegment::handleMiddleMouseUp(S32 x, S32 y, MASK mask) { return FALSE; }
+BOOL LLTextSegment::handleRightMouseDown(S32 x, S32 y, MASK mask) { return FALSE; }
+BOOL LLTextSegment::handleRightMouseUp(S32 x, S32 y, MASK mask) { return FALSE; }
+BOOL LLTextSegment::handleDoubleClick(S32 x, S32 y, MASK mask) { return FALSE; }
+BOOL LLTextSegment::handleHover(S32 x, S32 y, MASK mask) { return FALSE; }
+BOOL LLTextSegment::handleScrollWheel(S32 x, S32 y, S32 clicks) { return FALSE; }
+BOOL LLTextSegment::handleToolTip(S32 x, S32 y, MASK mask) { return FALSE; }
+std::string LLTextSegment::getName() const { return ""; }
+void LLTextSegment::onMouseCaptureLost() {}
+void LLTextSegment::screenPointToLocal(S32 screen_x, S32 screen_y, S32* local_x, S32* local_y) const {}
+void LLTextSegment::localPointToScreen(S32 local_x, S32 local_y, S32* screen_x, S32* screen_y) const {}
+BOOL LLTextSegment::hasMouseCapture() { return FALSE; }
+
+//
+// LLNormalTextSegment
+//
+
+LLNormalTextSegment::LLNormalTextSegment( LLStyleConstSP style, S32 start, S32 end, LLTextBase& editor )
+: LLTextSegment(start, end),
+ mStyle( style ),
+ mToken(NULL),
+ mEditor(editor)
+{
+ mFontHeight = llceil(mStyle->getFont()->getLineHeight());
+
+ LLUIImagePtr image = mStyle->getImage();
+ if (image.notNull())
+ {
+ mImageLoadedConnection = image->addLoadedCallback(boost::bind(&LLTextBase::needsReflow, &mEditor, start));
+ }
+}
+
+LLNormalTextSegment::LLNormalTextSegment( const LLColor4& color, S32 start, S32 end, LLTextBase& editor, BOOL is_visible)
+: LLTextSegment(start, end),
+ mToken(NULL),
+ mEditor(editor)
+{
+ mStyle = new LLStyle(LLStyle::Params().visible(is_visible).color(color));
+
+ mFontHeight = llceil(mStyle->getFont()->getLineHeight());
+}
+
+LLNormalTextSegment::~LLNormalTextSegment()
+{
+ mImageLoadedConnection.disconnect();
+}
+
+
+F32 LLNormalTextSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRect& draw_rect)
+{
+ if( end - start > 0 )
+ {
+ return drawClippedSegment( getStart() + start, getStart() + end, selection_start, selection_end, draw_rect);
+ }
+ return draw_rect.mLeft;
+}
+
+// Draws a single text segment, reversing the color for selection if needed.
+F32 LLNormalTextSegment::drawClippedSegment(S32 seg_start, S32 seg_end, S32 selection_start, S32 selection_end, LLRect rect)
+{
+ F32 alpha = LLViewDrawContext::getCurrentContext().mAlpha;
+
+ const LLWString &text = mEditor.getWText();
+
+ F32 right_x = rect.mLeft;
+ if (!mStyle->isVisible())
+ {
+ return right_x;
+ }
+
+ const LLFontGL* font = mStyle->getFont();
+
+ LLColor4 color = (mEditor.getReadOnly() ? mStyle->getReadOnlyColor() : mStyle->getColor()) % alpha;
+
+ if( selection_start > seg_start )
+ {
+ // Draw normally
+ S32 start = seg_start;
+ S32 end = llmin( selection_start, seg_end );
+ S32 length = end - start;
+ font->render(text, start,
+ rect,
+ color,
+ LLFontGL::LEFT, mEditor.mVAlign,
+ LLFontGL::NORMAL,
+ mStyle->getShadowType(),
+ length,
+ &right_x,
+ mEditor.getUseEllipses());
+ }
+ rect.mLeft = (S32)ceil(right_x);
+
+ if( (selection_start < seg_end) && (selection_end > seg_start) )
+ {
+ // Draw reversed
+ S32 start = llmax( selection_start, seg_start );
+ S32 end = llmin( selection_end, seg_end );
+ S32 length = end - start;
+
+ font->render(text, start,
+ rect,
+ mStyle->getSelectedColor().get(),
+ LLFontGL::LEFT, mEditor.mVAlign,
+ LLFontGL::NORMAL,
+ LLFontGL::NO_SHADOW,
+ length,
+ &right_x,
+ mEditor.getUseEllipses());
+ }
+ rect.mLeft = (S32)ceil(right_x);
+ if( selection_end < seg_end )
+ {
+ // Draw normally
+ S32 start = llmax( selection_end, seg_start );
+ S32 end = seg_end;
+ S32 length = end - start;
+ font->render(text, start,
+ rect,
+ color,
+ LLFontGL::LEFT, mEditor.mVAlign,
+ LLFontGL::NORMAL,
+ mStyle->getShadowType(),
+ length,
+ &right_x,
+ mEditor.getUseEllipses());
+ }
+ return right_x;
+}
+
+BOOL LLNormalTextSegment::handleHover(S32 x, S32 y, MASK mask)
+{
+ if (getStyle() && getStyle()->isLink())
+ {
+ // Only process the click if it's actually in this segment, not to the right of the end-of-line.
+ if(mEditor.getSegmentAtLocalPos(x, y, false) == this)
+ {
+ LLUI::getWindow()->setCursor(UI_CURSOR_HAND);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+BOOL LLNormalTextSegment::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+ if (getStyle() && getStyle()->isLink())
+ {
+ // Only process the click if it's actually in this segment, not to the right of the end-of-line.
+ if(mEditor.getSegmentAtLocalPos(x, y, false) == this)
+ {
+ mEditor.createUrlContextMenu(x, y, getStyle()->getLinkHREF());
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+BOOL LLNormalTextSegment::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ if (getStyle() && getStyle()->isLink())
+ {
+ // Only process the click if it's actually in this segment, not to the right of the end-of-line.
+ if(mEditor.getSegmentAtLocalPos(x, y, false) == this)
+ {
+ // eat mouse down event on hyperlinks, so we get the mouse up
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+BOOL LLNormalTextSegment::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ if (getStyle() && getStyle()->isLink())
+ {
+ // Only process the click if it's actually in this segment, not to the right of the end-of-line.
+ if(mEditor.getSegmentAtLocalPos(x, y, false) == this)
+ {
+ LLUrlAction::clickAction(getStyle()->getLinkHREF());
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+BOOL LLNormalTextSegment::handleToolTip(S32 x, S32 y, MASK mask)
+{
+ std::string msg;
+ // do we have a tooltip for a loaded keyword (for script editor)?
+ if (mToken && !mToken->getToolTip().empty())
+ {
+ const LLWString& wmsg = mToken->getToolTip();
+ LLToolTipMgr::instance().show(wstring_to_utf8str(wmsg));
+ return TRUE;
+ }
+ // or do we have an explicitly set tooltip (e.g., for Urls)
+ if (!mTooltip.empty())
+ {
+ LLToolTipMgr::instance().show(mTooltip);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+void LLNormalTextSegment::setToolTip(const std::string& tooltip)
+{
+ // we cannot replace a keyword tooltip that's loaded from a file
+ if (mToken)
+ {
+ llwarns << "LLTextSegment::setToolTip: cannot replace keyword tooltip." << llendl;
+ return;
+ }
+ mTooltip = tooltip;
+}
+
+bool LLNormalTextSegment::getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height) const
+{
+ height = 0;
+ width = 0;
+ if (num_chars > 0)
+ {
+ height = mFontHeight;
+ const LLWString &text = mEditor.getWText();
+ // if last character is a newline, then return true, forcing line break
+ width = mStyle->getFont()->getWidth(text.c_str(), mStart + first_char, num_chars);
+ }
+ return false;
+}
+
+S32 LLNormalTextSegment::getOffset(S32 segment_local_x_coord, S32 start_offset, S32 num_chars, bool round) const
+{
+ const LLWString &text = mEditor.getWText();
+ return mStyle->getFont()->charFromPixelOffset(text.c_str(), mStart + start_offset,
+ (F32)segment_local_x_coord,
+ F32_MAX,
+ num_chars,
+ round);
+}
+
+S32 LLNormalTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars) const
+{
+ const LLWString &text = mEditor.getWText();
+
+ LLUIImagePtr image = mStyle->getImage();
+ if( image.notNull())
+ {
+ num_pixels = llmax(0, num_pixels - image->getWidth());
+ }
+
+ S32 last_char = mEnd;
+
+ // set max characters to length of segment, or to first newline
+ max_chars = llmin(max_chars, last_char - (mStart + segment_offset));
+
+ // if no character yet displayed on this line, don't require word wrapping since
+ // we can just move to the next line, otherwise insist on it so we make forward progress
+ LLFontGL::EWordWrapStyle word_wrap_style = (line_offset == 0)
+ ? LLFontGL::WORD_BOUNDARY_IF_POSSIBLE
+ : LLFontGL::ONLY_WORD_BOUNDARIES;
+ S32 num_chars = mStyle->getFont()->maxDrawableChars(text.c_str() + segment_offset + mStart,
+ (F32)num_pixels,
+ max_chars,
+ word_wrap_style);
+
+ if (num_chars == 0
+ && line_offset == 0
+ && max_chars > 0)
+ {
+ // If at the beginning of a line, and a single character won't fit, draw it anyway
+ num_chars = 1;
+ }
+
+ // include *either* the EOF or newline character in this run of text
+ // but not both
+ S32 last_char_in_run = mStart + segment_offset + num_chars;
+ // check length first to avoid indexing off end of string
+ if (last_char_in_run < mEnd
+ && (last_char_in_run >= mEditor.getLength() ))
+ {
+ num_chars++;
+ }
+ return num_chars;
+}
+
+void LLNormalTextSegment::dump() const
+{
+ llinfos << "Segment [" <<
+// mColor.mV[VX] << ", " <<
+// mColor.mV[VY] << ", " <<
+// mColor.mV[VZ] << "]\t[" <<
+ mStart << ", " <<
+ getEnd() << "]" <<
+ llendl;
+}
+
+//
+// LLOnHoverChangeableTextSegment
+//
+
+LLOnHoverChangeableTextSegment::LLOnHoverChangeableTextSegment( LLStyleConstSP style, LLStyleConstSP normal_style, S32 start, S32 end, LLTextBase& editor ):
+ LLNormalTextSegment(normal_style, start, end, editor),
+ mHoveredStyle(style),
+ mNormalStyle(normal_style){}
+
+/*virtual*/
+F32 LLOnHoverChangeableTextSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRect& draw_rect)
+{
+ F32 result = LLNormalTextSegment::draw(start, end, selection_start, selection_end, draw_rect);
+ if (end == mEnd - mStart)
+ {
+ mStyle = mNormalStyle;
+ }
+ return result;
+}
+
+/*virtual*/
+BOOL LLOnHoverChangeableTextSegment::handleHover(S32 x, S32 y, MASK mask)
+{
+ mStyle = mHoveredStyle;
+ return LLNormalTextSegment::handleHover(x, y, mask);
+}
+
+
+//
+// LLInlineViewSegment
+//
+
+LLInlineViewSegment::LLInlineViewSegment(const Params& p, S32 start, S32 end)
+: LLTextSegment(start, end),
+ mView(p.view),
+ mForceNewLine(p.force_newline),
+ mLeftPad(p.left_pad),
+ mRightPad(p.right_pad),
+ mTopPad(p.top_pad),
+ mBottomPad(p.bottom_pad)
+{
+}
+
+LLInlineViewSegment::~LLInlineViewSegment()
+{
+ mView->die();
+}
+
+bool LLInlineViewSegment::getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height) const
+{
+ if (first_char == 0 && num_chars == 0)
+ {
+ // we didn't fit on a line, the widget will fall on the next line
+ // so dimensions here are 0
+ width = 0;
+ height = 0;
+ }
+ else
+ {
+ width = mLeftPad + mRightPad + mView->getRect().getWidth();
+ height = mBottomPad + mTopPad + mView->getRect().getHeight();
+ }
+
+ return false;
+}
+
+S32 LLInlineViewSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars) const
+{
+ // if putting a widget anywhere but at the beginning of a line
+ // and the widget doesn't fit or mForceNewLine is true
+ // then return 0 chars for that line, and all characters for the next
+ if (line_offset != 0
+ && (mForceNewLine || num_pixels < mView->getRect().getWidth()))
+ {
+ return 0;
+ }
+ else
+ {
+ return mEnd - mStart;
+ }
+}
+
+void LLInlineViewSegment::updateLayout(const LLTextBase& editor)
+{
+ LLRect start_rect = editor.getDocRectFromDocIndex(mStart);
+ mView->setOrigin(start_rect.mLeft + mLeftPad, start_rect.mBottom + mBottomPad);
+}
+
+F32 LLInlineViewSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRect& draw_rect)
+{
+ // return padded width of widget
+ // widget is actually drawn during mDocumentView's draw()
+ return (F32)(draw_rect.mLeft + mView->getRect().getWidth() + mLeftPad + mRightPad);
+}
+
+void LLInlineViewSegment::unlinkFromDocument(LLTextBase* editor)
+{
+ editor->removeDocumentChild(mView);
+}
+
+void LLInlineViewSegment::linkToDocument(LLTextBase* editor)
+{
+ editor->addDocumentChild(mView);
+}
+
+LLLineBreakTextSegment::LLLineBreakTextSegment(S32 pos):LLTextSegment(pos,pos+1)
+{
+ LLStyleSP s( new LLStyle(LLStyle::Params().visible(true)));
+
+ mFontHeight = llceil(s->getFont()->getLineHeight());
+}
+LLLineBreakTextSegment::LLLineBreakTextSegment(LLStyleConstSP style,S32 pos):LLTextSegment(pos,pos+1)
+{
+ mFontHeight = llceil(style->getFont()->getLineHeight());
+}
+LLLineBreakTextSegment::~LLLineBreakTextSegment()
+{
+}
+bool LLLineBreakTextSegment::getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height) const
+{
+ width = 0;
+ height = mFontHeight;
+
+ return true;
+}
+S32 LLLineBreakTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars) const
+{
+ return 1;
+}
+F32 LLLineBreakTextSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRect& draw_rect)
+{
+ return draw_rect.mLeft;
+}
+
+LLImageTextSegment::LLImageTextSegment(LLStyleConstSP style,S32 pos,class LLTextBase& editor)
+: LLTextSegment(pos,pos+1),
+ mStyle( style ),
+ mEditor(editor)
+{
+}
+
+LLImageTextSegment::~LLImageTextSegment()
+{
+}
+
+static const S32 IMAGE_HPAD = 3;
+
+bool LLImageTextSegment::getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height) const
+{
+ width = 0;
+ height = llceil(mStyle->getFont()->getLineHeight());;
+
+ LLUIImagePtr image = mStyle->getImage();
+ if( num_chars>0 && image.notNull())
+ {
+ width += image->getWidth() + IMAGE_HPAD;
+ height = llmax(height, image->getHeight() + IMAGE_HPAD );
+ }
+ return false;
+}
+
+S32 LLImageTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars) const
+{
+ LLUIImagePtr image = mStyle->getImage();
+ S32 image_width = image->getWidth();
+ if(line_offset == 0 || num_pixels>image_width + IMAGE_HPAD)
+ {
+ return 1;
+ }
+ return 0;
+}
+
+F32 LLImageTextSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRect& draw_rect)
+{
+ if ( (start >= 0) && (end <= mEnd - mStart))
+ {
+ LLColor4 color = LLColor4::white % mEditor.getDrawContext().mAlpha;
+ LLUIImagePtr image = mStyle->getImage();
+ S32 style_image_height = image->getHeight();
+ S32 style_image_width = image->getWidth();
+ // Text is drawn from the top of the draw_rect downward
+
+ S32 text_center = draw_rect.mTop - (draw_rect.getHeight() / 2);
+ // Align image to center of draw rect
+ S32 image_bottom = text_center - (style_image_height / 2);
+ image->draw(draw_rect.mLeft, image_bottom,
+ style_image_width, style_image_height, color);
+
+ const S32 IMAGE_HPAD = 3;
+ return draw_rect.mLeft + style_image_width + IMAGE_HPAD;
+ }
+ return 0.0;
+}
+
diff --git a/indra/llui/lltextbase.h b/indra/llui/lltextbase.h
new file mode 100644
index 0000000000..e5dfecad54
--- /dev/null
+++ b/indra/llui/lltextbase.h
@@ -0,0 +1,569 @@
+/**
+ * @file lltextbase.h
+ * @author Martin Reddy
+ * @brief The base class of text box/editor, providing Url handling support
+ *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLTEXTBASE_H
+#define LL_LLTEXTBASE_H
+
+#include "v4color.h"
+#include "lleditmenuhandler.h"
+#include "llstyle.h"
+#include "llkeywords.h"
+#include "llpanel.h"
+
+#include <string>
+#include <vector>
+#include <set>
+
+#include <boost/signals2.hpp>
+
+class LLContextMenu;
+class LLUrlMatch;
+
+///
+/// A text segment is used to specify a subsection of a text string
+/// that should be formatted differently, such as a hyperlink. It
+/// includes a start/end offset from the start of the string, a
+/// style to render with, an optional tooltip, etc.
+///
+class LLTextSegment : public LLRefCount, public LLMouseHandler
+{
+public:
+ LLTextSegment(S32 start, S32 end) : mStart(start), mEnd(end){};
+ virtual ~LLTextSegment();
+
+ virtual bool getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height) const;
+ virtual S32 getOffset(S32 segment_local_x_coord, S32 start_offset, S32 num_chars, bool round) const;
+ virtual S32 getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars) const;
+ virtual void updateLayout(const class LLTextBase& editor);
+ virtual F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRect& draw_rect);
+ virtual bool canEdit() const;
+ virtual void unlinkFromDocument(class LLTextBase* editor);
+ virtual void linkToDocument(class LLTextBase* editor);
+
+ virtual const LLColor4& getColor() const;
+ //virtual void setColor(const LLColor4 &color);
+ virtual LLStyleConstSP getStyle() const;
+ virtual void setStyle(LLStyleConstSP style);
+ virtual void setToken( LLKeywordToken* token );
+ virtual LLKeywordToken* getToken() const;
+ virtual void setToolTip(const std::string& tooltip);
+ virtual void dump() const;
+
+ // LLMouseHandler interface
+ /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleMiddleMouseDown(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleMiddleMouseUp(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleRightMouseUp(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleDoubleClick(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleScrollWheel(S32 x, S32 y, S32 clicks);
+ /*virtual*/ BOOL handleToolTip(S32 x, S32 y, MASK mask);
+ /*virtual*/ std::string getName() const;
+ /*virtual*/ void onMouseCaptureLost();
+ /*virtual*/ void screenPointToLocal(S32 screen_x, S32 screen_y, S32* local_x, S32* local_y) const;
+ /*virtual*/ void localPointToScreen(S32 local_x, S32 local_y, S32* screen_x, S32* screen_y) const;
+ /*virtual*/ BOOL hasMouseCapture();
+
+ S32 getStart() const { return mStart; }
+ void setStart(S32 start) { mStart = start; }
+ S32 getEnd() const { return mEnd; }
+ void setEnd( S32 end ) { mEnd = end; }
+
+protected:
+ S32 mStart;
+ S32 mEnd;
+};
+
+class LLNormalTextSegment : public LLTextSegment
+{
+public:
+ LLNormalTextSegment( LLStyleConstSP style, S32 start, S32 end, LLTextBase& editor );
+ LLNormalTextSegment( const LLColor4& color, S32 start, S32 end, LLTextBase& editor, BOOL is_visible = TRUE);
+ ~LLNormalTextSegment();
+
+ /*virtual*/ bool getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height) const;
+ /*virtual*/ S32 getOffset(S32 segment_local_x_coord, S32 start_offset, S32 num_chars, bool round) const;
+ /*virtual*/ S32 getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars) const;
+ /*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*/ LLStyleConstSP getStyle() const { return mStyle; }
+ /*virtual*/ void setStyle(LLStyleConstSP style) { mStyle = style; }
+ /*virtual*/ void setToken( LLKeywordToken* token ) { mToken = token; }
+ /*virtual*/ LLKeywordToken* getToken() const { return mToken; }
+ /*virtual*/ BOOL getToolTip( std::string& msg ) const;
+ /*virtual*/ void setToolTip(const std::string& tooltip);
+ /*virtual*/ void dump() const;
+
+ /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleToolTip(S32 x, S32 y, MASK mask);
+
+protected:
+ F32 drawClippedSegment(S32 seg_start, S32 seg_end, S32 selection_start, S32 selection_end, LLRect rect);
+
+protected:
+ class LLTextBase& mEditor;
+ LLStyleConstSP mStyle;
+ S32 mFontHeight;
+ LLKeywordToken* mToken;
+ std::string mTooltip;
+ boost::signals2::connection mImageLoadedConnection;
+};
+
+// Text segment that changes it's style depending of mouse pointer position ( is it inside or outside segment)
+class LLOnHoverChangeableTextSegment : public LLNormalTextSegment
+{
+public:
+ LLOnHoverChangeableTextSegment( LLStyleConstSP style, LLStyleConstSP normal_style, S32 start, S32 end, LLTextBase& editor );
+ /*virtual*/ F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRect& draw_rect);
+ /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask);
+protected:
+ // Style used for text when mouse pointer is over segment
+ LLStyleConstSP mHoveredStyle;
+ // Style used for text when mouse pointer is outside segment
+ LLStyleConstSP mNormalStyle;
+
+};
+
+class LLIndexSegment : public LLTextSegment
+{
+public:
+ LLIndexSegment() : LLTextSegment(0, 0) {}
+};
+
+class LLInlineViewSegment : public LLTextSegment
+{
+public:
+ struct Params : public LLInitParam::Block<Params>
+ {
+ Mandatory<LLView*> view;
+ Optional<bool> force_newline;
+ Optional<S32> left_pad,
+ right_pad,
+ bottom_pad,
+ top_pad;
+ };
+
+ LLInlineViewSegment(const Params& p, S32 start, S32 end);
+ ~LLInlineViewSegment();
+ /*virtual*/ bool getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height) const;
+ /*virtual*/ S32 getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars) const;
+ /*virtual*/ void updateLayout(const class LLTextBase& editor);
+ /*virtual*/ F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRect& draw_rect);
+ /*virtual*/ bool canEdit() const { return false; }
+ /*virtual*/ void unlinkFromDocument(class LLTextBase* editor);
+ /*virtual*/ void linkToDocument(class LLTextBase* editor);
+
+private:
+ S32 mLeftPad;
+ S32 mRightPad;
+ S32 mTopPad;
+ S32 mBottomPad;
+ LLView* mView;
+ bool mForceNewLine;
+};
+
+class LLLineBreakTextSegment : public LLTextSegment
+{
+public:
+
+ LLLineBreakTextSegment(LLStyleConstSP style,S32 pos);
+ LLLineBreakTextSegment(S32 pos);
+ ~LLLineBreakTextSegment();
+ bool getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height) const;
+ S32 getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars) const;
+ F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRect& draw_rect);
+
+private:
+ S32 mFontHeight;
+};
+
+class LLImageTextSegment : public LLTextSegment
+{
+public:
+ LLImageTextSegment(LLStyleConstSP style,S32 pos,class LLTextBase& editor);
+ ~LLImageTextSegment();
+ bool getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height) const;
+ S32 getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars) const;
+ F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRect& draw_rect);
+
+private:
+ class LLTextBase& mEditor;
+ LLStyleConstSP mStyle;
+};
+
+typedef LLPointer<LLTextSegment> LLTextSegmentPtr;
+
+///
+/// The LLTextBase class provides a base class for all text fields, such
+/// as LLTextEditor and LLTextBox. It implements shared functionality
+/// such as Url highlighting and opening.
+///
+class LLTextBase
+: public LLUICtrl,
+ protected LLEditMenuHandler
+{
+public:
+ friend class LLTextSegment;
+ friend class LLNormalTextSegment;
+
+ struct LineSpacingParams : public LLInitParam::Choice<LineSpacingParams>
+ {
+ Alternative<F32> multiple;
+ Alternative<S32> pixels;
+ LineSpacingParams();
+ };
+
+ struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
+ {
+ Optional<LLUIColor> cursor_color,
+ text_color,
+ text_readonly_color,
+ bg_readonly_color,
+ bg_writeable_color,
+ bg_focus_color,
+ text_selected_color,
+ bg_selected_color;
+
+ Optional<bool> bg_visible,
+ border_visible,
+ track_end,
+ read_only,
+ allow_scroll,
+ plain_text,
+ wrap,
+ use_ellipses,
+ parse_urls,
+ parse_highlights,
+ clip_partial;
+
+ Optional<S32> v_pad,
+ h_pad;
+
+
+ Optional<LineSpacingParams>
+ line_spacing;
+
+ Optional<S32> max_text_length;
+
+ Optional<LLFontGL::ShadowType> font_shadow;
+
+ Params();
+ };
+
+ // LLMouseHandler interface
+ /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleMiddleMouseDown(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleMiddleMouseUp(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleRightMouseUp(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleDoubleClick(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleScrollWheel(S32 x, S32 y, S32 clicks);
+ /*virtual*/ BOOL handleToolTip(S32 x, S32 y, MASK mask);
+
+ // LLView interface
+ /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
+ /*virtual*/ void draw();
+
+ // LLUICtrl interface
+ /*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
+ bool getWordWrap() { return mWordWrap; }
+ bool getUseEllipses() { return mUseEllipses; }
+ bool truncate(); // returns true of truncation occurred
+
+ // TODO: move into LLTextSegment?
+ void createUrlContextMenu(S32 x, S32 y, const std::string &url); // create a popup context menu for the given Url
+
+ // Text accessors
+ // TODO: add optional style parameter
+ virtual void setText(const LLStringExplicit &utf8str , const LLStyle::Params& input_params = LLStyle::Params()); // uses default style
+ virtual std::string getText() const;
+ void setMaxTextLength(S32 length) { mMaxTextByteLength = length; }
+
+ // wide-char versions
+ void setWText(const LLWString& text);
+ const LLWString& getWText() const;
+
+ void appendText(const std::string &new_text, bool prepend_newline, const LLStyle::Params& input_params = LLStyle::Params());
+ // force reflow of text
+ void needsReflow(S32 index = 0);
+
+ S32 getLength() const { return getWText().length(); }
+ S32 getLineCount() const { return mLineInfoList.size(); }
+
+ void addDocumentChild(LLView* view);
+ void removeDocumentChild(LLView* view);
+ const LLView* getDocumentView() const { return mDocumentView; }
+ LLRect getVisibleTextRect() { return mVisibleTextRect; }
+ LLRect getTextBoundingRect();
+ LLRect getVisibleDocumentRect() const;
+
+ S32 getVPad() { return mVPad; }
+ S32 getHPad() { return mHPad; }
+
+ S32 getDocIndexFromLocalCoord( S32 local_x, S32 local_y, BOOL round, bool hit_past_end_of_line = true) const;
+ LLRect getLocalRectFromDocIndex(S32 pos) const;
+ LLRect getDocRectFromDocIndex(S32 pos) const;
+
+ void setReadOnly(bool read_only) { mReadOnly = read_only; }
+ bool getReadOnly() { return mReadOnly; }
+
+ void setPlainText(bool value) { mPlainText = value;}
+ bool getPlainText() const { return mPlainText; }
+
+ // cursor manipulation
+ bool setCursor(S32 row, S32 column);
+ bool setCursorPos(S32 cursor_pos, bool keep_cursor_offset = false);
+ void startOfLine();
+ void endOfLine();
+ void startOfDoc();
+ void endOfDoc();
+ void changePage( S32 delta );
+ void changeLine( S32 delta );
+
+ bool scrolledToStart();
+ bool scrolledToEnd();
+
+ const LLFontGL* getDefaultFont() const { return mDefaultFont; }
+
+ virtual void appendLineBreakSegment(const LLStyle::Params& style_params);
+ virtual void appendImageSegment(const LLStyle::Params& style_params);
+ virtual void appendWidget(const LLInlineViewSegment::Params& params, const std::string& text, bool allow_undo);
+ boost::signals2::connection setURLClickedCallback(const commit_signal_t::slot_type& cb);
+
+protected:
+ // helper structs
+ struct compare_bottom;
+ struct compare_top;
+ struct line_end_compare;
+ typedef std::vector<LLTextSegmentPtr> segment_vec_t;
+
+ // Abstract inner base class representing an undoable editor command.
+ // Concrete sub-classes can be defined for operations such as insert, remove, etc.
+ // Used as arguments to the execute() method below.
+ class TextCmd
+ {
+ public:
+ TextCmd( S32 pos, BOOL group_with_next, LLTextSegmentPtr segment = LLTextSegmentPtr() )
+ : mPos(pos),
+ mGroupWithNext(group_with_next)
+ {
+ if (segment.notNull())
+ {
+ mSegments.push_back(segment);
+ }
+ }
+ virtual ~TextCmd() {}
+ virtual BOOL execute(LLTextBase* editor, S32* delta) = 0;
+ virtual S32 undo(LLTextBase* editor) = 0;
+ virtual S32 redo(LLTextBase* editor) = 0;
+ virtual BOOL canExtend(S32 pos) const { return FALSE; }
+ virtual void blockExtensions() {}
+ virtual BOOL extendAndExecute( LLTextBase* editor, S32 pos, llwchar c, S32* delta ) { llassert(0); return 0; }
+ virtual BOOL hasExtCharValue( llwchar value ) const { return FALSE; }
+
+ // Defined here so they can access protected LLTextEditor editing methods
+ S32 insert(LLTextBase* editor, S32 pos, const LLWString &wstr) { return editor->insertStringNoUndo( pos, wstr, &mSegments ); }
+ S32 remove(LLTextBase* editor, S32 pos, S32 length) { return editor->removeStringNoUndo( pos, length ); }
+ S32 overwrite(LLTextBase* editor, S32 pos, llwchar wc) { return editor->overwriteCharNoUndo(pos, wc); }
+
+ S32 getPosition() const { return mPos; }
+ BOOL groupWithNext() const { return mGroupWithNext; }
+
+ protected:
+ const S32 mPos;
+ BOOL mGroupWithNext;
+ segment_vec_t mSegments;
+ };
+
+ struct compare_segment_end
+ {
+ bool operator()(const LLTextSegmentPtr& a, const LLTextSegmentPtr& b) const;
+ };
+ typedef std::multiset<LLTextSegmentPtr, compare_segment_end> segment_set_t;
+
+ // protected member variables
+ // List of offsets and segment index of the start of each line. Always has at least one node (0).
+ struct line_info
+ {
+ line_info(S32 index_start, S32 index_end, LLRect rect, S32 line_num);
+ S32 mDocIndexStart;
+ S32 mDocIndexEnd;
+ LLRect mRect;
+ S32 mLineNum; // actual line count (ignoring soft newlines due to word wrap)
+ };
+ typedef std::vector<line_info> line_list_t;
+
+ // member functions
+ LLTextBase(const Params &p);
+ virtual ~LLTextBase();
+ void initFromParams(const Params& p);
+ virtual void onValueChange(S32 start, S32 end);
+
+ // draw methods
+ void drawSelectionBackground(); // draws the black box behind the selected text
+ void drawCursor();
+ void drawText();
+
+ // modify contents
+ S32 insertStringNoUndo(S32 pos, const LLWString &wstr, segment_vec_t* segments = NULL); // returns num of chars actually inserted
+ S32 removeStringNoUndo(S32 pos, S32 length);
+ S32 overwriteCharNoUndo(S32 pos, llwchar wc);
+ void appendAndHighlightText(const std::string &new_text, S32 highlight_part, const LLStyle::Params& stylep, bool underline_on_hover_only = false);
+
+
+ // manage segments
+ void getSegmentAndOffset( S32 startpos, segment_set_t::const_iterator* seg_iter, S32* offsetp ) const;
+ void getSegmentAndOffset( S32 startpos, segment_set_t::iterator* seg_iter, S32* offsetp );
+ LLTextSegmentPtr getSegmentAtLocalPos( S32 x, S32 y, bool hit_past_end_of_line = true);
+ segment_set_t::iterator getSegIterContaining(S32 index);
+ segment_set_t::const_iterator getSegIterContaining(S32 index) const;
+ void clearSegments();
+ void createDefaultSegment();
+ virtual void updateSegments();
+ void insertSegment(LLTextSegmentPtr segment_to_insert);
+ const LLStyle::Params& getDefaultStyleParams();
+
+ // manage lines
+ S32 getLineStart( S32 line ) const;
+ S32 getLineEnd( S32 line ) const;
+ S32 getLineNumFromDocIndex( S32 doc_index, bool include_wordwrap = true) const;
+ S32 getLineOffsetFromDocIndex( S32 doc_index, bool include_wordwrap = true) const;
+ S32 getFirstVisibleLine() const;
+ std::pair<S32, S32> getVisibleLines(bool fully_visible = false);
+ S32 getLeftOffset(S32 width);
+ void reflow();
+
+ // cursor
+ void updateCursorXPos();
+ void setCursorAtLocalPos( S32 local_x, S32 local_y, bool round, bool keep_cursor_offset=false );
+ S32 getEditableIndex(S32 index, bool increasing_direction); // constraint cursor to editable segments of document
+ void resetCursorBlink() { mCursorBlinkTimer.reset(); }
+ void updateScrollFromCursor();
+
+ // text selection
+ bool hasSelection() const { return (mSelectionStart !=mSelectionEnd); }
+ void startSelection();
+ void endSelection();
+
+ // misc
+ void updateRects();
+ void needsScroll() { mScrollNeeded = TRUE; }
+ void replaceUrlLabel(const std::string &url, const std::string &label);
+
+ void appendTextImpl(const std::string &new_text, const LLStyle::Params& input_params = LLStyle::Params());
+ void appendAndHighlightTextImpl(const std::string &new_text, S32 highlight_part, const LLStyle::Params& style_params, bool underline_on_hover_only = false);
+
+
+protected:
+ // text segmentation and flow
+ segment_set_t mSegments;
+ line_list_t mLineInfoList;
+ LLRect mVisibleTextRect; // The rect in which text is drawn. Excludes borders.
+ LLRect mTextBoundingRect;
+
+ // default text style
+ LLStyle::Params mDefaultStyle;
+ bool mStyleDirty;
+ const LLFontGL* const mDefaultFont; // font that is used when none specified, can only be set by constructor
+ const LLFontGL::ShadowType mFontShadow; // shadow style, can only be set by constructor
+
+ // colors
+ LLUIColor mCursorColor;
+ LLUIColor mFgColor;
+ LLUIColor mReadOnlyFgColor;
+ LLUIColor mWriteableBgColor;
+ LLUIColor mReadOnlyBgColor;
+ LLUIColor mFocusBgColor;
+ LLUIColor mTextSelectedColor;
+ LLUIColor mSelectedBGColor;
+
+ // cursor
+ S32 mCursorPos; // I-beam is just after the mCursorPos-th character.
+ S32 mDesiredXPixel; // X pixel position where the user wants the cursor to be
+ LLFrameTimer mCursorBlinkTimer; // timer that controls cursor blinking
+
+ // selection
+ S32 mSelectionStart;
+ S32 mSelectionEnd;
+
+ BOOL mIsSelecting; // Are we in the middle of a drag-select?
+
+ // configuration
+ S32 mHPad; // padding on left of text
+ S32 mVPad; // padding above text
+ LLFontGL::HAlign mHAlign;
+ LLFontGL::VAlign mVAlign;
+ F32 mLineSpacingMult; // multiple of line height used as space for a single line of text (e.g. 1.5 to get 50% padding)
+ S32 mLineSpacingPixels; // padding between lines
+ bool mBorderVisible;
+ bool mParseHTML; // make URLs interactive
+ bool mParseHighlights; // highlight user-defined keywords
+ bool mWordWrap;
+ bool mUseEllipses;
+ bool mTrackEnd; // if true, keeps scroll position at end of document during resize
+ bool mReadOnly;
+ bool mBGVisible; // render background?
+ bool mClipPartial; // false if we show lines that are partially inside bounding rect
+ bool mPlainText; // didn't use Image or Icon segments
+ S32 mMaxTextByteLength; // Maximum length mText is allowed to be in bytes
+
+ // support widgets
+ LLContextMenu* mPopupMenu;
+ LLView* mDocumentView;
+ class LLScrollContainer* mScroller;
+
+ // transient state
+ S32 mReflowIndex; // index at which to start reflow. S32_MAX indicates no reflow needed.
+ bool mScrollNeeded; // need to change scroll region because of change to cursor position
+ S32 mScrollIndex; // index of first character to keep visible in scroll region
+
+ // Fired when a URL link is clicked
+ commit_signal_t* mURLClickSignal;
+
+};
+
+#endif
diff --git a/indra/llui/lltextbox.cpp b/indra/llui/lltextbox.cpp
index 464e4be809..6a905b7ec0 100644
--- a/indra/llui/lltextbox.cpp
+++ b/indra/llui/lltextbox.cpp
@@ -2,113 +2,68 @@
* @file lltextbox.cpp
* @brief A text display widget
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
-#define INSTANTIATE_GETCHILD_TEXTBOX
-
#include "linden_common.h"
+
+#define LLTEXTBOX_CPP
#include "lltextbox.h"
+
#include "lluictrlfactory.h"
#include "llfocusmgr.h"
#include "llwindow.h"
+#include "llurlregistry.h"
+#include "llstyle.h"
-template LLTextBox* LLView::getChild<LLTextBox>( const std::string& name, BOOL recurse, BOOL create_if_missing ) const;
-
-static LLDefaultWidgetRegistry::Register<LLTextBox> r("text");
-
-LLTextBox::Params::Params()
-: text_color("text_color"),
- length("length"),
- type("type"),
- highlight_on_hover("hover", false),
- border_visible("border_visible", false),
- border_drop_shadow_visible("border_drop_shadow_visible", false),
- bg_visible("bg_visible", false),
- use_ellipses("use_ellipses"),
- word_wrap("word_wrap", false),
- drop_shadow_visible("drop_shadow_visible"),
- hover_color("hover_color"),
- disabled_color("disabled_color"),
- background_color("background_color"),
- border_color("border_color"),
- v_pad("v_pad", 0),
- h_pad("h_pad", 0),
- line_spacing("line_spacing", 0),
- text("text"),
- font_shadow("font_shadow", LLFontGL::NO_SHADOW)
-{}
+static LLDefaultChildRegistry::Register<LLTextBox> r("text");
+
+// Compiler optimization, generate extern template
+template class LLTextBox* LLView::getChild<class LLTextBox>(
+ const std::string& name, BOOL recurse) const;
LLTextBox::LLTextBox(const LLTextBox::Params& p)
-: LLUICtrl(p),
- mFontGL(p.font),
- mHoverActive( p.highlight_on_hover ),
- mHasHover( FALSE ),
- mBackgroundVisible( p.bg_visible ),
- mBorderVisible( p.border_visible ),
- mShadowType( p.font_shadow ),
- mBorderDropShadowVisible( p.border_drop_shadow_visible ),
- mUseEllipses( p.use_ellipses ),
- mHPad(p.h_pad),
- mVPad(p.v_pad),
- mVAlign( LLFontGL::TOP ),
- mClickedCallback(NULL),
- mTextColor(p.text_color()),
- mDisabledColor(p.disabled_color()),
- mBackgroundColor(p.background_color()),
- mBorderColor(p.border_color()),
- mHoverColor(p.hover_color()),
- mHAlign(p.font_halign),
- mLineSpacing(p.line_spacing),
- mWordWrap( p.word_wrap ),
- mDidWordWrap(FALSE),
- mFontStyle(LLFontGL::getStyleFromString(p.font.style))
-{
- setText( p.text() );
-}
+: LLTextBase(p),
+ mClickedCallback(NULL)
+{}
+
+LLTextBox::~LLTextBox()
+{}
BOOL LLTextBox::handleMouseDown(S32 x, S32 y, MASK mask)
{
- BOOL handled = FALSE;
+ BOOL handled = LLTextBase::handleMouseDown(x, y, mask);
- // HACK: Only do this if there actually is a click callback, so that
- // overly large text boxes in the older UI won't start eating clicks.
- if (mClickedCallback)
+ if (getSoundFlags() & MOUSE_DOWN)
{
- handled = TRUE;
+ make_ui_sound("UISndClick");
+ }
+ if (!handled && mClickedCallback)
+ {
// Route future Mouse messages here preemptively. (Release on mouse up.)
gFocusMgr.setMouseCapture( this );
-
- if (getSoundFlags() & MOUSE_DOWN)
- {
- make_ui_sound("UISndClick");
- }
+
+ handled = TRUE;
}
return handled;
@@ -118,299 +73,107 @@ BOOL LLTextBox::handleMouseUp(S32 x, S32 y, MASK mask)
{
BOOL handled = FALSE;
- // We only handle the click if the click both started and ended within us
-
- // HACK: Only do this if there actually is a click callback, so that
- // overly large text boxes in the older UI won't start eating clicks.
- if (mClickedCallback
- && hasMouseCapture())
+ if (getSoundFlags() & MOUSE_UP)
{
- handled = TRUE;
+ make_ui_sound("UISndClickRelease");
+ }
+ // We only handle the click if the click both started and ended within us
+ if (hasMouseCapture())
+ {
// Release the mouse
gFocusMgr.setMouseCapture( NULL );
- if (getSoundFlags() & MOUSE_UP)
- {
- make_ui_sound("UISndClickRelease");
- }
-
- // DO THIS AT THE VERY END to allow the button to be destroyed as a result of being clicked.
- // If mouseup in the widget, it's been clicked
- if (mClickedCallback)
+ // DO THIS AT THE VERY END to allow the button to be destroyed
+ // as a result of being clicked. If mouseup in the widget,
+ // it's been clicked
+ if (mClickedCallback && !handled)
{
mClickedCallback();
+ handled = TRUE;
}
}
+ else
+ {
+ handled = LLTextBase::handleMouseUp(x, y, mask);
+ }
return handled;
}
BOOL LLTextBox::handleHover(S32 x, S32 y, MASK mask)
{
- BOOL handled = LLView::handleHover(x,y,mask);
- if(mHoverActive)
+ BOOL handled = LLTextBase::handleHover(x, y, mask);
+ if (!handled && mClickedCallback)
{
- mHasHover = TRUE; // This should be set every frame during a hover.
- getWindow()->setCursor(UI_CURSOR_ARROW);
+ // Clickable text boxes change the cursor to a hand
+ LLUI::getWindow()->setCursor(UI_CURSOR_HAND);
+ return TRUE;
}
-
- return (handled || mHasHover);
+ return handled;
}
-void LLTextBox::setText(const LLStringExplicit& text)
+void LLTextBox::setEnabled(BOOL enabled)
{
- if(mWordWrap && !mDidWordWrap)
+ // just treat enabled as read-only flag
+ bool read_only = !enabled;
+ if (read_only != mReadOnly)
{
- setWrappedText(text);
- }
- else
- {
- mText.assign(text);
- setLineLengths();
+ LLTextBase::setReadOnly(read_only);
+ updateSegments();
}
}
-void LLTextBox::setLineLengths()
+void LLTextBox::setText(const LLStringExplicit& text , const LLStyle::Params& input_params )
{
- mLineLengthList.clear();
+ // does string argument insertion
+ mText.assign(text);
- std::string::size_type cur = 0;
- std::string::size_type len = mText.getWString().size();
-
- while (cur < len)
- {
- std::string::size_type end = mText.getWString().find('\n', cur);
- std::string::size_type runLen;
-
- if (end == std::string::npos)
- {
- runLen = len - cur;
- cur = len;
- }
- else
- {
- runLen = end - cur;
- cur = end + 1; // skip the new line character
- }
-
- mLineLengthList.push_back( (S32)runLen );
- }
+ LLTextBase::setText(mText.getString(), input_params );
}
-void LLTextBox::setWrappedText(const LLStringExplicit& in_text, F32 max_width)
+void LLTextBox::setClickedCallback( boost::function<void (void*)> cb, void* userdata /*= NULL */ )
{
- if (max_width < 0.0f)
- {
- max_width = (F32)getRect().getWidth();
- }
-
- LLWString wtext = utf8str_to_wstring(in_text);
- LLWString final_wtext;
-
- LLWString::size_type cur = 0;;
- LLWString::size_type len = wtext.size();
- F32 line_height = mFontGL->getLineHeight();
- S32 line_num = 1;
- while (cur < len)
- {
- LLWString::size_type end = wtext.find('\n', cur);
- if (end == LLWString::npos)
- {
- end = len;
- }
-
- LLWString::size_type runLen = end - cur;
- if (runLen > 0)
- {
- LLWString run(wtext, cur, runLen);
- LLWString::size_type useLen =
- mFontGL->maxDrawableChars(run.c_str(), max_width, runLen, TRUE);
-
- final_wtext.append(wtext, cur, useLen);
- cur += useLen;
- // not enough room to add any more characters
- if (useLen == 0) break;
- }
-
- if (cur < len)
- {
- if (wtext[cur] == '\n')
- {
- cur += 1;
- }
- line_num +=1;
- // Don't wrap the last line if the text is going to spill off
- // the bottom of the rectangle. Assume we prefer to run off
- // the right edge.
- // *TODO: Is this the right behavior?
- if((line_num-1)*line_height <= (F32)getRect().getHeight())
- {
- final_wtext += '\n';
- }
- }
- }
-
- mDidWordWrap = TRUE;
- std::string final_text = wstring_to_utf8str(final_wtext);
- setText(final_text);
-
+ mClickedCallback = boost::bind(cb, userdata);
}
S32 LLTextBox::getTextPixelWidth()
{
- S32 max_line_width = 0;
- if( mLineLengthList.size() > 0 )
- {
- S32 cur_pos = 0;
- for (std::vector<S32>::iterator iter = mLineLengthList.begin();
- iter != mLineLengthList.end(); ++iter)
- {
- S32 line_length = *iter;
- S32 line_width = mFontGL->getWidth( mText.getWString().c_str(), cur_pos, line_length );
- if( line_width > max_line_width )
- {
- max_line_width = line_width;
- }
- cur_pos += line_length+1;
- }
- }
- else
- {
- max_line_width = mFontGL->getWidth(mText.getWString().c_str());
- }
- return max_line_width;
+ return getTextBoundingRect().getWidth();
}
S32 LLTextBox::getTextPixelHeight()
{
- S32 num_lines = mLineLengthList.size();
- if( num_lines < 1 )
- {
- num_lines = 1;
- }
- return (S32)(num_lines * mFontGL->getLineHeight());
+ return getTextBoundingRect().getHeight();
}
-void LLTextBox::setValue(const LLSD& value )
-{
- mDidWordWrap = FALSE;
- setText(value.asString());
+
+LLSD LLTextBox::getValue() const
+{
+ return LLSD(getText());
}
BOOL LLTextBox::setTextArg( const std::string& key, const LLStringExplicit& text )
{
mText.setArg(key, text);
- setLineLengths();
+ LLTextBase::setText(mText.getString());
+
return TRUE;
}
-void LLTextBox::draw()
-{
- if (mBorderVisible)
- {
- gl_rect_2d_offset_local(getLocalRect(), 2, FALSE);
- }
-
- if( mBorderDropShadowVisible )
- {
- static LLUICachedControl<LLColor4> color_drop_shadow ("ColorDropShadow", *(new LLColor4));
- static LLUICachedControl<S32> drop_shadow_tooltip ("DropShadowTooltip", 0);
- gl_drop_shadow(0, getRect().getHeight(), getRect().getWidth(), 0,
- color_drop_shadow, drop_shadow_tooltip);
- }
-
- if (mBackgroundVisible)
- {
- LLRect r( 0, getRect().getHeight(), getRect().getWidth(), 0 );
- gl_rect_2d( r, mBackgroundColor.get() );
- }
-
- S32 text_x = 0;
- switch( mHAlign )
- {
- case LLFontGL::LEFT:
- text_x = mHPad;
- break;
- case LLFontGL::HCENTER:
- text_x = getRect().getWidth() / 2;
- break;
- case LLFontGL::RIGHT:
- text_x = getRect().getWidth() - mHPad;
- break;
- }
-
- S32 text_y = getRect().getHeight() - mVPad;
-
- if ( getEnabled() )
- {
- if(mHasHover)
- {
- drawText( text_x, text_y, mHoverColor.get() );
- }
- else
- {
- drawText( text_x, text_y, mTextColor.get() );
- }
- }
- else
- {
- drawText( text_x, text_y, mDisabledColor.get() );
- }
- if (sDebugRects)
- {
- drawDebugRect();
- }
-
- //// *HACK: also draw debug rectangles around currently-being-edited LLView, and any elements that are being highlighted by GUI preview code (see LLFloaterUIPreview)
- //std::set<LLView*>::iterator iter = std::find(sPreviewHighlightedElements.begin(), sPreviewHighlightedElements.end(), this);
- //if ((sEditingUI && this == sEditingUIView) || (iter != sPreviewHighlightedElements.end() && sDrawPreviewHighlights))
- //{
- // drawDebugRect();
- //}
+void LLTextBox::reshapeToFitText()
+{
+ reflow();
- mHasHover = FALSE; // This is reset every frame.
+ S32 width = getTextPixelWidth();
+ S32 height = getTextPixelHeight();
+ reshape( width + 2 * mHPad, height + 2 * mVPad, FALSE );
}
-void LLTextBox::reshape(S32 width, S32 height, BOOL called_from_parent)
-{
- // reparse line lengths
- setLineLengths();
- LLView::reshape(width, height, called_from_parent);
-}
-void LLTextBox::drawText( S32 x, S32 y, const LLColor4& color )
+void LLTextBox::onUrlLabelUpdated(const std::string &url, const std::string &label)
{
- if( mLineLengthList.empty() )
- {
- mFontGL->render(mText.getWString(), 0, (F32)x, (F32)y, color,
- mHAlign, mVAlign,
- mFontStyle,
- mShadowType,
- S32_MAX, getRect().getWidth(), NULL, TRUE, mUseEllipses);
- }
- else
- {
- S32 cur_pos = 0;
- for (std::vector<S32>::iterator iter = mLineLengthList.begin();
- iter != mLineLengthList.end(); ++iter)
- {
- S32 line_length = *iter;
- mFontGL->render(mText.getWString(), cur_pos, (F32)x, (F32)y, color,
- mHAlign, mVAlign,
- mFontStyle,
- mShadowType,
- line_length, getRect().getWidth(), NULL, TRUE, mUseEllipses );
- cur_pos += line_length + 1;
- y -= llfloor(mFontGL->getLineHeight()) + mLineSpacing;
- }
- }
+ needsReflow();
}
-void LLTextBox::reshapeToFitText()
-{
- S32 width = getTextPixelWidth();
- S32 height = getTextPixelHeight();
- reshape( width + 2 * mHPad, height + 2 * mVPad );
-}
diff --git a/indra/llui/lltextbox.h b/indra/llui/lltextbox.h
index aae538a221..071e18c638 100644
--- a/indra/llui/lltextbox.h
+++ b/indra/llui/lltextbox.h
@@ -2,168 +2,83 @@
* @file lltextbox.h
* @brief A single text item display
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LL_LLTEXTBOX_H
#define LL_LLTEXTBOX_H
-#include "lluictrl.h"
-#include "v4color.h"
-#include "llstring.h"
#include "lluistring.h"
+#include "lltextbase.h"
-
-class LLTextBox
-: public LLUICtrl
+class LLTextBox :
+ public LLTextBase
{
public:
// *TODO: Add callback to Params
typedef boost::function<void (void)> callback_t;
- struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
- {
- Optional<std::string> text;
-
- Optional<bool> highlight_on_hover,
- border_visible,
- border_drop_shadow_visible,
- bg_visible,
- use_ellipses,
- word_wrap;
-
- Optional<LLFontGL::ShadowType> font_shadow;
-
- Deprecated drop_shadow_visible,
- type,
- length;
-
- Optional<LLUIColor> text_color,
- hover_color,
- disabled_color,
- background_color,
- border_color;
-
- Optional<S32> v_pad,
- h_pad,
- line_spacing;
-
- Params();
- };
+ struct Params : public LLInitParam::Block<Params, LLTextBase::Params>
+ {};
protected:
LLTextBox(const Params&);
friend class LLUICtrlFactory;
public:
- virtual ~LLTextBox() {}
+ virtual ~LLTextBox();
- virtual void draw();
- virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
+ /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask);
- virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
- virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
- virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+ /*virtual*/ void setEnabled(BOOL enabled);
- void setColor( const LLColor4& c ) { mTextColor = c; }
- void setDisabledColor( const LLColor4& c) { mDisabledColor = c; }
- void setBackgroundColor( const LLColor4& c) { mBackgroundColor = c; }
- void setBorderColor( const LLColor4& c) { mBorderColor = c; }
-
- void setHoverColor( const LLColor4& c ) { mHoverColor = c; }
- void setHoverActive( BOOL active ) { mHoverActive = active; }
-
- void setText( const LLStringExplicit& text );
- void setWrappedText(const LLStringExplicit& text, F32 max_width = -1.f); // -1 means use existing control width
- void setUseEllipses( BOOL use_ellipses ) { mUseEllipses = use_ellipses; }
+ /*virtual*/ void setText( const LLStringExplicit& text, const LLStyle::Params& input_params = LLStyle::Params() );
- void setBackgroundVisible(BOOL visible) { mBackgroundVisible = visible; }
- void setBorderVisible(BOOL visible) { mBorderVisible = visible; }
- void setBorderDropshadowVisible(BOOL visible){ mBorderDropShadowVisible = visible; }
- void setHPad(S32 pixels) { mHPad = pixels; }
- void setVPad(S32 pixels) { mVPad = pixels; }
void setRightAlign() { mHAlign = LLFontGL::RIGHT; }
void setHAlign( LLFontGL::HAlign align ) { mHAlign = align; }
- void setClickedCallback( boost::function<void (void*)> cb, void* userdata = NULL ){ mClickedCallback = boost::bind(cb, userdata); } // mouse down and up within button
-
- const LLFontGL* getFont() const { return mFontGL; }
+ void setClickedCallback( boost::function<void (void*)> cb, void* userdata = NULL );
void reshapeToFitText();
- const std::string& getText() const { return mText.getString(); }
S32 getTextPixelWidth();
S32 getTextPixelHeight();
- virtual void setValue(const LLSD& value );
- virtual LLSD getValue() const { return LLSD(getText()); }
- virtual BOOL setTextArg( const std::string& key, const LLStringExplicit& text );
-
-private:
- void setLineLengths();
- void drawText(S32 x, S32 y, const LLColor4& color );
-
- LLUIString mText;
- const LLFontGL* mFontGL;
- LLUIColor mTextColor;
- LLUIColor mDisabledColor;
- LLUIColor mBackgroundColor;
- LLUIColor mBorderColor;
- LLUIColor mHoverColor;
+ /*virtual*/ LLSD getValue() const;
+ /*virtual*/ BOOL setTextArg( const std::string& key, const LLStringExplicit& text );
- BOOL mHoverActive;
- BOOL mHasHover;
- BOOL mBackgroundVisible;
- BOOL mBorderVisible;
- BOOL mWordWrap;
- BOOL mDidWordWrap;
-
- U8 mFontStyle; // style bit flags for font
- LLFontGL::ShadowType mShadowType;
- BOOL mBorderDropShadowVisible;
- BOOL mUseEllipses;
-
- S32 mLineSpacing;
-
- S32 mHPad;
- S32 mVPad;
- LLFontGL::HAlign mHAlign;
- LLFontGL::VAlign mVAlign;
+protected:
+ void onUrlLabelUpdated(const std::string &url, const std::string &label);
- std::vector<S32> mLineLengthList;
- callback_t mClickedCallback;
+ LLUIString mText;
+ callback_t mClickedCallback;
};
-#ifdef LL_WINDOWS
-#ifndef INSTANTIATE_GETCHILD_TEXTBOX
-#pragma warning (disable : 4231)
-extern template LLTextBox* LLView::getChild<LLTextBox>( const std::string& name, BOOL recurse, BOOL create_if_missing ) const;
-#endif
+// Build time optimization, generate once in .cpp file
+#ifndef LLTEXTBOX_CPP
+extern template class LLTextBox* LLView::getChild<class LLTextBox>(
+ const std::string& name, BOOL recurse) const;
#endif
#endif
diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp
index 34bced064e..94bf716e7d 100644
--- a/indra/llui/lltexteditor.cpp
+++ b/indra/llui/lltexteditor.cpp
@@ -1,32 +1,25 @@
/**
* @file lltexteditor.cpp
- * @brief LLTextEditor base class
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -34,9 +27,13 @@
#include "linden_common.h"
+#define LLTEXTEDITOR_CPP
#include "lltexteditor.h"
+#include "llfontfreetype.h" // for LLFontFreetype::FIRST_CHAR
#include "llfontgl.h"
+#include "llgl.h" // LLGLSUIDefault()
+#include "lllocalcliprect.h"
#include "llrender.h"
#include "llui.h"
#include "lluictrlfactory.h"
@@ -45,7 +42,6 @@
#include "lltimer.h"
#include "llmath.h"
-#include "audioengine.h"
#include "llclipboard.h"
#include "llscrollbar.h"
#include "llstl.h"
@@ -55,55 +51,56 @@
#include "llundo.h"
#include "llviewborder.h"
#include "llcontrol.h"
-#include "llimagegl.h"
#include "llwindow.h"
#include "lltextparser.h"
+#include "llscrollcontainer.h"
+#include "llpanel.h"
+#include "llurlregistry.h"
+#include "lltooltip.h"
+#include "llmenugl.h"
+
#include <queue>
+#include "llcombobox.h"
//
// Globals
//
-static LLDefaultWidgetRegistry::Register<LLTextEditor> r("simple_text_editor");
+static LLDefaultChildRegistry::Register<LLTextEditor> r("simple_text_editor");
+
+// Compiler optimization, generate extern template
+template class LLTextEditor* LLView::getChild<class LLTextEditor>(
+ const std::string& name, BOOL recurse) const;
//
// Constants
//
const S32 UI_TEXTEDITOR_LINE_NUMBER_MARGIN = 32;
const S32 UI_TEXTEDITOR_LINE_NUMBER_DIGITS = 4;
-const F32 CURSOR_FLASH_DELAY = 1.0f; // in seconds
-const S32 CURSOR_THICKNESS = 2;
const S32 SPACES_PER_TAB = 4;
-
-LLColor4 LLTextEditor::mLinkColor = LLColor4::blue;
-void (* LLTextEditor::mURLcallback)(const std::string&) = NULL;
-bool (* LLTextEditor::mSecondlifeURLcallback)(const std::string&) = NULL;
-bool (* LLTextEditor::mSecondlifeURLcallbackRightClick)(const std::string&) = NULL;
-
-
///////////////////////////////////////////////////////////////////
-class LLTextEditor::LLTextCmdInsert : public LLTextEditor::LLTextCmd
+class LLTextEditor::TextCmdInsert : public LLTextBase::TextCmd
{
public:
- LLTextCmdInsert(S32 pos, BOOL group_with_next, const LLWString &ws)
- : LLTextCmd(pos, group_with_next), mWString(ws)
+ TextCmdInsert(S32 pos, BOOL group_with_next, const LLWString &ws, LLTextSegmentPtr segment)
+ : TextCmd(pos, group_with_next, segment), mWString(ws)
{
}
- virtual ~LLTextCmdInsert() {}
- virtual BOOL execute( LLTextEditor* editor, S32* delta )
+ virtual ~TextCmdInsert() {}
+ virtual BOOL execute( LLTextBase* editor, S32* delta )
{
*delta = insert(editor, getPosition(), mWString );
LLWStringUtil::truncate(mWString, *delta);
//mWString = wstring_truncate(mWString, *delta);
return (*delta != 0);
}
- virtual S32 undo( LLTextEditor* editor )
+ virtual S32 undo( LLTextBase* editor )
{
remove(editor, getPosition(), mWString.length() );
return getPosition();
}
- virtual S32 redo( LLTextEditor* editor )
+ virtual S32 redo( LLTextBase* editor )
{
insert(editor, getPosition(), mWString );
return getPosition() + mWString.length();
@@ -114,11 +111,11 @@ private:
};
///////////////////////////////////////////////////////////////////
-class LLTextEditor::LLTextCmdAddChar : public LLTextEditor::LLTextCmd
+class LLTextEditor::TextCmdAddChar : public LLTextBase::TextCmd
{
public:
- LLTextCmdAddChar( S32 pos, BOOL group_with_next, llwchar wc)
- : LLTextCmd(pos, group_with_next), mWString(1, wc), mBlockExtensions(FALSE)
+ TextCmdAddChar( S32 pos, BOOL group_with_next, llwchar wc, LLTextSegmentPtr segment)
+ : TextCmd(pos, group_with_next, segment), mWString(1, wc), mBlockExtensions(FALSE)
{
}
virtual void blockExtensions()
@@ -127,16 +124,19 @@ public:
}
virtual BOOL canExtend(S32 pos) const
{
+ // cannot extend text with custom segments
+ if (!mSegments.empty()) return FALSE;
+
return !mBlockExtensions && (pos == getPosition() + (S32)mWString.length());
}
- virtual BOOL execute( LLTextEditor* editor, S32* delta )
+ virtual BOOL execute( LLTextBase* editor, S32* delta )
{
*delta = insert(editor, getPosition(), mWString);
LLWStringUtil::truncate(mWString, *delta);
//mWString = wstring_truncate(mWString, *delta);
return (*delta != 0);
}
- virtual BOOL extendAndExecute( LLTextEditor* editor, S32 pos, llwchar wc, S32* delta )
+ virtual BOOL extendAndExecute( LLTextBase* editor, S32 pos, llwchar wc, S32* delta )
{
LLWString ws;
ws += wc;
@@ -148,12 +148,12 @@ public:
}
return (*delta != 0);
}
- virtual S32 undo( LLTextEditor* editor )
+ virtual S32 undo( LLTextBase* editor )
{
remove(editor, getPosition(), mWString.length() );
return getPosition();
}
- virtual S32 redo( LLTextEditor* editor )
+ virtual S32 redo( LLTextBase* editor )
{
insert(editor, getPosition(), mWString );
return getPosition() + mWString.length();
@@ -167,25 +167,25 @@ private:
///////////////////////////////////////////////////////////////////
-class LLTextEditor::LLTextCmdOverwriteChar : public LLTextEditor::LLTextCmd
+class LLTextEditor::TextCmdOverwriteChar : public LLTextBase::TextCmd
{
public:
- LLTextCmdOverwriteChar( S32 pos, BOOL group_with_next, llwchar wc)
- : LLTextCmd(pos, group_with_next), mChar(wc), mOldChar(0) {}
+ TextCmdOverwriteChar( S32 pos, BOOL group_with_next, llwchar wc)
+ : TextCmd(pos, group_with_next), mChar(wc), mOldChar(0) {}
- virtual BOOL execute( LLTextEditor* editor, S32* delta )
+ virtual BOOL execute( LLTextBase* editor, S32* delta )
{
- mOldChar = editor->getWChar(getPosition());
+ mOldChar = editor->getWText()[getPosition()];
overwrite(editor, getPosition(), mChar);
*delta = 0;
return TRUE;
}
- virtual S32 undo( LLTextEditor* editor )
+ virtual S32 undo( LLTextBase* editor )
{
overwrite(editor, getPosition(), mOldChar);
return getPosition();
}
- virtual S32 redo( LLTextEditor* editor )
+ virtual S32 redo( LLTextBase* editor )
{
overwrite(editor, getPosition(), mChar);
return getPosition()+1;
@@ -198,25 +198,26 @@ private:
///////////////////////////////////////////////////////////////////
-class LLTextEditor::LLTextCmdRemove : public LLTextEditor::LLTextCmd
+class LLTextEditor::TextCmdRemove : public LLTextBase::TextCmd
{
public:
- LLTextCmdRemove( S32 pos, BOOL group_with_next, S32 len ) :
- LLTextCmd(pos, group_with_next), mLen(len)
+ TextCmdRemove( S32 pos, BOOL group_with_next, S32 len, segment_vec_t& segments ) :
+ TextCmd(pos, group_with_next), mLen(len)
{
+ std::swap(mSegments, segments);
}
- virtual BOOL execute( LLTextEditor* editor, S32* delta )
+ virtual BOOL execute( LLTextBase* editor, S32* delta )
{
- mWString = editor->getWSubString(getPosition(), mLen);
+ mWString = editor->getWText().substr(getPosition(), mLen);
*delta = remove(editor, getPosition(), mLen );
return (*delta != 0);
}
- virtual S32 undo( LLTextEditor* editor )
+ virtual S32 undo( LLTextBase* editor )
{
- insert(editor, getPosition(), mWString );
+ insert(editor, getPosition(), mWString);
return getPosition() + mWString.length();
}
- virtual S32 redo( LLTextEditor* editor )
+ virtual S32 redo( LLTextBase* editor )
{
remove(editor, getPosition(), mLen );
return getPosition();
@@ -228,352 +229,107 @@ private:
///////////////////////////////////////////////////////////////////
-LLTextEditor::LLTextEditor(const LLTextEditor::Params& p)
- : LLUICtrl(p, LLTextViewModelPtr(new LLTextViewModel)),
- mMaxTextByteLength( p.max_text_length ),
+LLTextEditor::Params::Params()
+: default_text("default_text"),
+ prevalidate_callback("prevalidate_callback"),
+ embedded_items("embedded_items", false),
+ ignore_tab("ignore_tab", true),
+ show_line_numbers("show_line_numbers", false),
+ default_color("default_color"),
+ 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),
mBaseDocIsPristine(TRUE),
mPristineCmd( NULL ),
mLastCmd( NULL ),
- mCursorPos( 0 ),
- mIsSelecting( FALSE ),
- mSelectionStart( 0 ),
- mSelectionEnd( 0 ),
- mScrolledToBottom( TRUE ),
- mOnScrollEndCallback( NULL ),
- mOnScrollEndData( NULL ),
- mCursorColor( p.cursor_color() ),
- mFgColor( p.text_color() ),
mDefaultColor( p.default_color() ),
- mReadOnlyFgColor( p.text_readonly_color() ),
- mWriteableBgColor( p.bg_writeable_color() ),
- mReadOnlyBgColor( p.bg_readonly_color() ),
- mFocusBgColor( p.bg_focus_color() ),
- mReadOnly(p.read_only),
- mWordWrap( p.word_wrap ),
- mShowLineNumbers ( FALSE ),
- mCommitOnFocusLost( FALSE ),
- mHideScrollbarForShortDocs( FALSE ),
- mTakesNonScrollClicks( p.takes_non_scroll_clicks ),
- mTrackBottom( p.track_bottom ),
- mAllowEmbeddedItems( p.allow_embedded_items ),
- mAcceptCallingCardNames(FALSE),
- mHandleEditKeysDirectly( FALSE ),
+ mShowLineNumbers ( p.show_line_numbers ),
+ mCommitOnFocusLost( p.commit_on_focus_lost),
+ mAllowEmbeddedItems( p.embedded_items ),
mMouseDownX(0),
mMouseDownY(0),
- mLastSelectionX(-1),
- mReflowNeeded(FALSE),
- mScrollNeeded(FALSE),
- mLastSelectionY(-1),
mTabsToNextField(p.ignore_tab),
- mGLFont(p.font),
- mGLFontStyle(LLFontGL::getStyleFromString(p.font.style))
+ mPrevalidateFunc(p.prevalidate_callback()),
+ mContextMenu(NULL),
+ mShowContextMenu(p.show_context_menu)
{
- static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
-
mSourceID.generate();
- // reset desired x cursor position
- mDesiredXPixel = -1;
-
- updateTextRect();
-
- S32 line_height = llround( mGLFont->getLineHeight() );
- S32 page_size = mTextRect.getHeight() / line_height;
-
- // Init the scrollbar
- LLRect scroll_rect;
- scroll_rect.setOriginAndSize(
- getRect().getWidth() - scrollbar_size,
- 1,
- scrollbar_size,
- getRect().getHeight() - 1);
- S32 lines_in_doc = getLineCount();
- LLScrollbar::Params sbparams;
- sbparams.name("Scrollbar");
- sbparams.rect(scroll_rect);
- sbparams.orientation(LLScrollbar::VERTICAL);
- sbparams.doc_size(lines_in_doc);
- sbparams.doc_pos(0);
- sbparams.page_size(page_size);
- sbparams.follows.flags(FOLLOWS_RIGHT | FOLLOWS_TOP | FOLLOWS_BOTTOM);
- mScrollbar = LLUICtrlFactory::create<LLScrollbar> (sbparams);
- mScrollbar->setOnScrollEndCallback(mOnScrollEndCallback, mOnScrollEndData);
- addChild(mScrollbar);
-
- static LLUICachedControl<S32> text_editor_border ("UITextEditorBorder", 0);
+ //FIXME: use image?
LLViewBorder::Params params;
- params.name("text ed border");
- params.rect(getLocalRect());
- params.bevel_type(LLViewBorder::BEVEL_IN);
- params.border_thickness(text_editor_border);
+ params.name = "text ed border";
+ params.rect = getLocalRect();
+ params.bevel_style = LLViewBorder::BEVEL_IN;
+ params.border_thickness = 1;
+ params.visible = p.border_visible;
mBorder = LLUICtrlFactory::create<LLViewBorder> (params);
addChild( mBorder );
- mBorder->setVisible(!p.hide_border);
- appendText(p.default_text, FALSE, FALSE);
+ setText(p.default_text());
- setHideScrollbarForShortDocs(p.hide_scrollbar);
-
- mParseHTML=FALSE;
- mHTML.clear();
+ if (mShowLineNumbers)
+ {
+ mHPad += UI_TEXTEDITOR_LINE_NUMBER_MARGIN;
+ updateRects();
+ }
}
void LLTextEditor::initFromParams( const LLTextEditor::Params& p)
{
- resetDirty(); // Update saved text state
- LLUICtrl::initFromParams(p);
- // HACK: work around enabled == readonly design bug -- RN
- // setEnabled will modify our read only status, so do this after
- // LLUICtrl::initFromParams
- if (p.read_only.isProvided())
- {
- mReadOnly = p.read_only;
- updateSegments();
- updateAllowingLanguageInput();
- }
+ LLTextBase::initFromParams(p);
+
// HACK: text editors always need to be enabled so that we can scroll
LLView::setEnabled(true);
-}
-LLTextEditor::~LLTextEditor()
-{
- gFocusMgr.releaseFocusIfNeeded( this ); // calls onCommit()
-
- // Route menu back to the default
- if( gEditMenuHandler == this )
+ if (p.commit_on_focus_lost.isProvided())
{
- gEditMenuHandler = NULL;
+ mCommitOnFocusLost = p.commit_on_focus_lost;
}
-
- // Scrollbar is deleted by LLView
- mHoverSegment = NULL;
- std::for_each(mSegments.begin(), mSegments.end(), DeletePointer());
-
- std::for_each(mUndoStack.begin(), mUndoStack.end(), DeletePointer());
-}
-
-LLTextViewModel* LLTextEditor::getViewModel() const
-{
- return (LLTextViewModel*)mViewModel.get();
-}
-
-void LLTextEditor::setTrackColor( const LLColor4& color )
-{
- mScrollbar->setTrackColor(color);
-}
-
-void LLTextEditor::setThumbColor( const LLColor4& color )
-{
- mScrollbar->setThumbColor(color);
+
+ updateAllowingLanguageInput();
}
-void LLTextEditor::updateLineStartList(S32 startpos)
+LLTextEditor::~LLTextEditor()
{
- updateSegments();
-
- bindEmbeddedChars(mGLFont);
-
- S32 seg_num = mSegments.size();
- S32 seg_idx = 0;
- S32 seg_offset = 0;
-
- if (!mLineStartList.empty())
- {
- getSegmentAndOffset(startpos, &seg_idx, &seg_offset);
- line_info t(seg_idx, seg_offset);
- line_list_t::iterator iter = std::upper_bound(mLineStartList.begin(), mLineStartList.end(), t, line_info_compare());
- if (iter != mLineStartList.begin()) --iter;
- seg_idx = iter->mSegment;
- seg_offset = iter->mOffset;
- mLineStartList.erase(iter, mLineStartList.end());
- }
-
- LLWString text(getWText());
- while( seg_idx < seg_num )
- {
- mLineStartList.push_back(line_info(seg_idx,seg_offset));
- BOOL line_ended = FALSE;
- S32 start_x = mShowLineNumbers ? UI_TEXTEDITOR_LINE_NUMBER_MARGIN : 0;
- S32 line_width = start_x;
- while(!line_ended && seg_idx < seg_num)
- {
- LLTextSegment* segment = mSegments[seg_idx];
- S32 start_idx = segment->getStart() + seg_offset;
- S32 end_idx = start_idx;
- while (end_idx < segment->getEnd() && text[end_idx] != '\n')
- {
- end_idx++;
- }
- if (start_idx == end_idx)
- {
- if (end_idx >= segment->getEnd())
- {
- // empty segment
- seg_idx++;
- seg_offset = 0;
- }
- else
- {
- // empty line
- line_ended = TRUE;
- seg_offset++;
- }
- }
- else
- {
- const llwchar* str = text.c_str() + start_idx;
- S32 drawn = mGLFont->maxDrawableChars(str, (F32)abs(mTextRect.getWidth()) - line_width,
- end_idx - start_idx, mWordWrap, mAllowEmbeddedItems );
- if( 0 == drawn && line_width == start_x)
- {
- // If at the beginning of a line, draw at least one character, even if it doesn't all fit.
- drawn = 1;
- }
- seg_offset += drawn;
- line_width += mGLFont->getWidth(str, 0, drawn, mAllowEmbeddedItems);
- end_idx = segment->getStart() + seg_offset;
- if (end_idx < segment->getEnd())
- {
- line_ended = TRUE;
- if (text[end_idx] == '\n')
- {
- seg_offset++; // skip newline
- }
- }
- else
- {
- // finished with segment
- seg_idx++;
- seg_offset = 0;
- }
- }
- }
- }
-
- unbindEmbeddedChars(mGLFont);
+ gFocusMgr.releaseFocusIfNeeded( this ); // calls onCommit() while LLTextEditor still valid
- mScrollbar->setDocSize( getLineCount() );
-
- if (mHideScrollbarForShortDocs)
- {
- BOOL short_doc = (mScrollbar->getDocSize() <= mScrollbar->getPageSize());
- mScrollbar->setVisible(!short_doc);
- }
+ // Scrollbar is deleted by LLView
+ std::for_each(mUndoStack.begin(), mUndoStack.end(), DeletePointer());
- // if scrolled to bottom, stay at bottom
- // unless user is selecting text
- // do this after updating page size
- if (mScrolledToBottom && mTrackBottom && !hasMouseCapture())
- {
- endOfDoc();
- }
+ // context menu is owned by menu holder, not us
+ //delete mContextMenu;
}
////////////////////////////////////////////////////////////
// LLTextEditor
// Public methods
-BOOL LLTextEditor::truncate()
+void LLTextEditor::setText(const LLStringExplicit &utf8str, const LLStyle::Params& input_params)
{
- BOOL did_truncate = FALSE;
-
- // First rough check - if we're less than 1/4th the size, we're OK
- if (getLength() >= S32(mMaxTextByteLength / 4))
- {
- // Have to check actual byte size
- LLWString text(getWText());
- S32 utf8_byte_size = wstring_utf8_length(text);
- if ( utf8_byte_size > mMaxTextByteLength )
+ // validate incoming text if necessary
+ if (mPrevalidateFunc)
+ {
+ LLWString test_text = utf8str_to_wstring(utf8str);
+ if (!mPrevalidateFunc(test_text))
{
- // Truncate safely in UTF-8
- std::string temp_utf8_text = wstring_to_utf8str(text);
- temp_utf8_text = utf8str_truncate( temp_utf8_text, mMaxTextByteLength );
- getViewModel()->setDisplay(utf8str_to_wstring( temp_utf8_text ));
- did_truncate = TRUE;
+ // not valid text, nothing to do
+ return;
}
}
- return did_truncate;
-}
-
-void LLTextEditor::setText(const LLStringExplicit &utf8str)
-{
- // LLStringUtil::removeCRLF(utf8str);
- mViewModel->setValue(utf8str_removeCRLF(utf8str));
-
- truncate();
blockUndo();
-
- setCursorPos(0);
- deselect();
-
- needsReflow();
-
- resetDirty();
-}
-
-void LLTextEditor::setWText(const LLWString &wtext)
-{
- getViewModel()->setDisplay(wtext);
-
- truncate();
- blockUndo();
-
- setCursorPos(0);
deselect();
- needsReflow();
+ LLTextBase::setText(utf8str, input_params);
resetDirty();
}
-// virtual
-void LLTextEditor::setValue(const LLSD& value)
-{
- setText(value.asString());
-}
-
-std::string LLTextEditor::getText() const
-{
- if (mAllowEmbeddedItems)
- {
- llwarns << "getText() called on text with embedded items (not supported)" << llendl;
- }
- return mViewModel->getValue().asString();
-}
-
-void LLTextEditor::setWordWrap(BOOL b)
-{
- mWordWrap = b;
-
- setCursorPos(0);
- deselect();
-
- needsReflow();
-}
-
-
-void LLTextEditor::setBorderVisible(BOOL b)
-{
- mBorder->setVisible(b);
-}
-
-BOOL LLTextEditor::isBorderVisible() const
-{
- return mBorder->getVisible();
-}
-
-void LLTextEditor::setHideScrollbarForShortDocs(BOOL b)
-{
- mHideScrollbarForShortDocs = b;
-
- if (mHideScrollbarForShortDocs)
- {
- BOOL short_doc = (mScrollbar->getDocSize() <= mScrollbar->getPageSize());
- mScrollbar->setVisible(!short_doc);
- }
-}
-
void LLTextEditor::selectNext(const std::string& search_text_in, BOOL case_insensitive, BOOL wrap)
{
if (search_text_in.empty())
@@ -596,7 +352,7 @@ void LLTextEditor::selectNext(const std::string& search_text_in, BOOL case_insen
if (selected_text == search_text)
{
// We already have this word selected, we are searching for the next.
- mCursorPos += search_text.size();
+ setCursorPos(mCursorPos + search_text.size());
}
}
@@ -659,9 +415,7 @@ BOOL LLTextEditor::replaceText(const std::string& search_text_in, const std::str
void LLTextEditor::replaceTextAll(const std::string& search_text, const std::string& replace_text, BOOL case_insensitive)
{
- S32 cur_pos = mScrollbar->getDocPos();
-
- setCursorPos(0);
+ startOfDoc();
selectNext(search_text, case_insensitive, FALSE);
BOOL replaced = TRUE;
@@ -669,14 +423,6 @@ void LLTextEditor::replaceTextAll(const std::string& search_text, const std::str
{
replaced = replaceText(search_text,replace_text, case_insensitive, FALSE);
}
-
- mScrollbar->setDocPos(cur_pos);
-}
-
-// Picks a new cursor position based on the screen size of text being drawn.
-void LLTextEditor::setCursorAtLocalPos( S32 local_x, S32 local_y, BOOL round )
-{
- setCursorPos(getCursorPosFromLocalCoord(local_x, local_y, round));
}
S32 LLTextEditor::prevWordPos(S32 cursorPos) const
@@ -686,7 +432,7 @@ S32 LLTextEditor::prevWordPos(S32 cursorPos) const
{
cursorPos--;
}
- while( (cursorPos > 0) && isPartOfWord( wtext[cursorPos-1] ) )
+ while( (cursorPos > 0) && LLWStringUtil::isPartOfWord( wtext[cursorPos-1] ) )
{
cursorPos--;
}
@@ -696,7 +442,7 @@ S32 LLTextEditor::prevWordPos(S32 cursorPos) const
S32 LLTextEditor::nextWordPos(S32 cursorPos) const
{
LLWString wtext(getWText());
- while( (cursorPos < getLength()) && isPartOfWord( wtext[cursorPos] ) )
+ while( (cursorPos < getLength()) && LLWStringUtil::isPartOfWord( wtext[cursorPos] ) )
{
cursorPos++;
}
@@ -707,198 +453,49 @@ S32 LLTextEditor::nextWordPos(S32 cursorPos) const
return cursorPos;
}
-S32 LLTextEditor::getLineStart( S32 line ) const
+const LLTextSegmentPtr LLTextEditor::getPreviousSegment() const
{
- S32 num_lines = getLineCount();
- if (num_lines == 0)
- {
- return 0;
- }
-
- line = llclamp(line, 0, num_lines-1);
- S32 segidx = mLineStartList[line].mSegment;
- S32 segoffset = mLineStartList[line].mOffset;
- LLTextSegment* seg = mSegments[segidx];
- S32 res = seg->getStart() + segoffset;
- if (res > seg->getEnd()) llerrs << "wtf" << llendl;
- return res;
-}
+ static LLPointer<LLIndexSegment> index_segment = new LLIndexSegment;
-// Given an offset into text (pos), find the corresponding line (from the start of the doc) and an offset into the line.
-void LLTextEditor::getLineAndOffset( S32 startpos, S32* linep, S32* offsetp ) const
-{
- if (mLineStartList.empty())
+ index_segment->setStart(mCursorPos);
+ index_segment->setEnd(mCursorPos);
+
+ // find segment index at character to left of cursor (or rightmost edge of selection)
+ segment_set_t::const_iterator it = mSegments.lower_bound(index_segment);
+
+ if (it != mSegments.end())
{
- *linep = 0;
- *offsetp = startpos;
+ return *it;
}
else
{
- S32 seg_idx, seg_offset;
- getSegmentAndOffset( startpos, &seg_idx, &seg_offset );
-
- line_info tline(seg_idx, seg_offset);
- line_list_t::const_iterator iter = std::upper_bound(mLineStartList.begin(), mLineStartList.end(), tline, line_info_compare());
- if (iter != mLineStartList.begin()) --iter;
- *linep = iter - mLineStartList.begin();
- S32 line_start = mSegments[iter->mSegment]->getStart() + iter->mOffset;
- *offsetp = startpos - line_start;
+ return LLTextSegmentPtr();
}
}
-void LLTextEditor::getSegmentAndOffset( S32 startpos, S32* segidxp, S32* offsetp ) const
-{
- if (mSegments.empty())
- {
- *segidxp = -1;
- *offsetp = startpos;
- }
-
- LLTextSegment tseg(startpos);
- segment_list_t::const_iterator seg_iter;
- seg_iter = std::upper_bound(mSegments.begin(), mSegments.end(), &tseg, LLTextSegment::compare());
- if (seg_iter != mSegments.begin()) --seg_iter;
- *segidxp = seg_iter - mSegments.begin();
- *offsetp = startpos - (*seg_iter)->getStart();
-}
-
-const LLTextSegment* LLTextEditor::getPreviousSegment() const
-{
- // find segment index at character to left of cursor (or rightmost edge of selection)
- S32 idx = llmax(0, getSegmentIdxAtOffset(mCursorPos) - 1);
- return idx >= 0 ? mSegments[idx] : NULL;
-}
-
-void LLTextEditor::getSelectedSegments(std::vector<const LLTextSegment*>& segments) const
+void LLTextEditor::getSelectedSegments(LLTextEditor::segment_vec_t& segments) const
{
S32 left = hasSelection() ? llmin(mSelectionStart, mSelectionEnd) : mCursorPos;
S32 right = hasSelection() ? llmax(mSelectionStart, mSelectionEnd) : mCursorPos;
- S32 first_idx = llmax(0, getSegmentIdxAtOffset(left));
- S32 last_idx = llmax(0, first_idx, getSegmentIdxAtOffset(right));
- for (S32 idx = first_idx; idx <= last_idx; ++idx)
- {
- segments.push_back(mSegments[idx]);
- }
+ return getSegmentsInRange(segments, left, right, true);
}
-S32 LLTextEditor::getCursorPosFromLocalCoord( S32 local_x, S32 local_y, BOOL round ) const
+void LLTextEditor::getSegmentsInRange(LLTextEditor::segment_vec_t& segments_out, S32 start, S32 end, bool include_partial) const
{
- if(mShowLineNumbers)
- {
- local_x -= UI_TEXTEDITOR_LINE_NUMBER_MARGIN;
- }
-
- // If round is true, if the position is on the right half of a character, the cursor
- // will be put to its right. If round is false, the cursor will always be put to the
- // character's left.
-
- // Figure out which line we're nearest to.
- S32 total_lines = getLineCount();
- S32 line_height = llround( mGLFont->getLineHeight() );
- S32 max_visible_lines = mTextRect.getHeight() / line_height;
- S32 scroll_lines = mScrollbar->getDocPos();
- S32 visible_lines = llmin( total_lines - scroll_lines, max_visible_lines ); // Lines currently visible
-
- //S32 line = S32( 0.5f + ((mTextRect.mTop - local_y) / mGLFont->getLineHeight()) );
- S32 line = (mTextRect.mTop - 1 - local_y) / line_height;
- if (line >= total_lines)
- {
- return getLength(); // past the end
- }
-
- line = llclamp( line, 0, visible_lines ) + scroll_lines;
-
- S32 line_start = getLineStart(line);
- S32 next_start = getLineStart(line+1);
- S32 line_end = (next_start != line_start) ? next_start - 1 : getLength();
+ segment_set_t::const_iterator first_it = getSegIterContaining(start);
+ segment_set_t::const_iterator end_it = getSegIterContaining(end - 1);
+ if (end_it != mSegments.end()) ++end_it;
- if(line_start == -1)
+ for (segment_set_t::const_iterator it = first_it; it != end_it; ++it)
{
- return 0;
- }
- else
- {
- S32 line_len = line_end - line_start;
- S32 pos;
- LLWString text(getWText());
-
- if (mAllowEmbeddedItems)
- {
- // Figure out which character we're nearest to.
- bindEmbeddedChars(mGLFont);
- pos = mGLFont->charFromPixelOffset(text.c_str(), line_start,
- (F32)(local_x - mTextRect.mLeft),
- (F32)(mTextRect.getWidth()),
- line_len,
- round, TRUE);
- unbindEmbeddedChars(mGLFont);
- }
- else
+ LLTextSegmentPtr segment = *it;
+ if (include_partial
+ || (segment->getStart() >= start
+ && segment->getEnd() <= end))
{
- pos = mGLFont->charFromPixelOffset(text.c_str(), line_start,
- (F32)(local_x - mTextRect.mLeft),
- (F32)mTextRect.getWidth(),
- line_len,
- round);
+ segments_out.push_back(segment);
}
-
- return line_start + pos;
- }
-}
-
-void LLTextEditor::setCursor(S32 row, S32 column)
-{
- LLWString text(getWText());
- const llwchar* doc = text.c_str();
- const char CR = 10;
- while(row--)
- {
- while (CR != *doc++);
- }
- doc += column;
- setCursorPos(doc - text.c_str());
-}
-
-void LLTextEditor::setCursorPos(S32 offset)
-{
- mCursorPos = llclamp(offset, 0, (S32)getLength());
- needsScroll();
- // reset desired x cursor position
- mDesiredXPixel = -1;
-}
-
-// virtual
-BOOL LLTextEditor::canDeselect() const
-{
- return hasSelection();
-}
-
-
-void LLTextEditor::deselect()
-{
- mSelectionStart = 0;
- mSelectionEnd = 0;
- mIsSelecting = FALSE;
-}
-
-
-void LLTextEditor::startSelection()
-{
- if( !mIsSelecting )
- {
- mIsSelecting = TRUE;
- mSelectionStart = mCursorPos;
- mSelectionEnd = mCursorPos;
- }
-}
-
-void LLTextEditor::endSelection()
-{
- if( mIsSelecting )
- {
- mIsSelecting = FALSE;
- mSelectionEnd = mCursorPos;
}
}
@@ -1006,7 +603,7 @@ void LLTextEditor::indentSelectedLines( S32 spaces )
}
right += delta_spaces;
- //text = mWText;
+ text = getWText();
// Find the next new line
while( (cur < right) && (text[cur] != '\n') )
@@ -1032,7 +629,7 @@ void LLTextEditor::indentSelectedLines( S32 spaces )
mSelectionStart = right;
mSelectionEnd = left;
}
- mCursorPos = mSelectionEnd;
+ setCursorPos(mSelectionEnd);
}
}
@@ -1047,64 +644,25 @@ void LLTextEditor::selectAll()
{
mSelectionStart = getLength();
mSelectionEnd = 0;
- mCursorPos = mSelectionEnd;
+ setCursorPos(mSelectionEnd);
+ updatePrimary();
}
-
-BOOL LLTextEditor::handleToolTip(S32 x, S32 y, std::string& msg, LLRect* sticky_rect_screen)
+BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask)
{
- for ( child_list_const_iter_t child_it = getChildList()->begin();
- child_it != getChildList()->end(); ++child_it)
- {
- LLView* viewp = *child_it;
- S32 local_x = x - viewp->getRect().mLeft;
- S32 local_y = y - viewp->getRect().mBottom;
- if( viewp->handleToolTip(local_x, local_y, msg, sticky_rect_screen ) )
- {
- return TRUE;
- }
- }
-
- if( mSegments.empty() )
- {
- return TRUE;
- }
+ BOOL handled = FALSE;
- const LLTextSegment* cur_segment = getSegmentAtLocalPos( x, y );
- if( cur_segment )
+ // set focus first, in case click callbacks want to change it
+ // RN: do we really need to have a tab stop?
+ if (hasTabStop())
{
- BOOL has_tool_tip = FALSE;
- has_tool_tip = cur_segment->getToolTip( msg );
-
- if( has_tool_tip )
- {
- // Just use a slop area around the cursor
- // Convert rect local to screen coordinates
- S32 SLOP = 8;
- localPointToScreen(
- x - SLOP, y - SLOP,
- &(sticky_rect_screen->mLeft), &(sticky_rect_screen->mBottom) );
- sticky_rect_screen->mRight = sticky_rect_screen->mLeft + 2 * SLOP;
- sticky_rect_screen->mTop = sticky_rect_screen->mBottom + 2 * SLOP;
- }
+ setFocus( TRUE );
}
- return TRUE;
-}
-
-BOOL LLTextEditor::handleScrollWheel(S32 x, S32 y, S32 clicks)
-{
- // Pretend the mouse is over the scrollbar
- return mScrollbar->handleScrollWheel( 0, 0, clicks );
-}
-
-BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask)
-{
- BOOL handled = FALSE;
// Let scrollbar have first dibs
- handled = LLView::childrenHandleMouseDown(x, y, mask) != NULL;
+ handled = LLTextBase::handleMouseDown(x, y, mask);
- if( !handled && mTakesNonScrollClicks)
+ if( !handled )
{
if (!(mask & MASK_SHIFT))
{
@@ -1118,31 +676,10 @@ BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask)
if (mask & MASK_SHIFT)
{
S32 old_cursor_pos = mCursorPos;
- setCursorAtLocalPos( x, y, TRUE );
+ setCursorAtLocalPos( x, y, true );
if (hasSelection())
{
- /* Mac-like behavior - extend selection towards the cursor
- if (mCursorPos < mSelectionStart
- && mCursorPos < mSelectionEnd)
- {
- // ...left of selection
- mSelectionStart = llmax(mSelectionStart, mSelectionEnd);
- mSelectionEnd = mCursorPos;
- }
- else if (mCursorPos > mSelectionStart
- && mCursorPos > mSelectionEnd)
- {
- // ...right of selection
- mSelectionStart = llmin(mSelectionStart, mSelectionEnd);
- mSelectionEnd = mCursorPos;
- }
- else
- {
- mSelectionEnd = mCursorPos;
- }
- */
- // Windows behavior
mSelectionEnd = mCursorPos;
}
else
@@ -1155,7 +692,7 @@ BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask)
}
else
{
- setCursorAtLocalPos( x, y, TRUE );
+ setCursorAtLocalPos( x, y, true );
startSelection();
}
gFocusMgr.setMouseCapture( this );
@@ -1164,26 +701,46 @@ BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask)
handled = TRUE;
}
- if (hasTabStop())
- {
- setFocus( TRUE );
- handled = TRUE;
- }
-
// Delay cursor flashing
- resetKeystrokeTimer();
+ resetCursorBlink();
return handled;
}
+BOOL LLTextEditor::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+ if (hasTabStop())
+ {
+ setFocus(TRUE);
+ }
+ // Prefer editor menu if it has selection. See EXT-6806.
+ if (hasSelection() || !LLTextBase::handleRightMouseDown(x, y, mask))
+ {
+ if(getShowContextMenu())
+ {
+ showContextMenu(x, y);
+ }
+ }
+ return TRUE;
+}
+
+
BOOL LLTextEditor::handleMiddleMouseDown(S32 x, S32 y, MASK mask)
{
- setFocus( TRUE );
- if( canPastePrimary() )
+ if (hasTabStop())
{
- setCursorAtLocalPos( x, y, TRUE );
- pastePrimary();
+ setFocus(TRUE);
+ }
+
+ if (!LLTextBase::handleMouseDown(x, y, mask))
+ {
+ if( canPastePrimary() )
+ {
+ setCursorAtLocalPos( x, y, true );
+ // does not rely on focus being set
+ pastePrimary();
+ }
}
return TRUE;
}
@@ -1191,34 +748,21 @@ BOOL LLTextEditor::handleMiddleMouseDown(S32 x, S32 y, MASK mask)
BOOL LLTextEditor::handleHover(S32 x, S32 y, MASK mask)
{
- static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
BOOL handled = FALSE;
- mHoverSegment = NULL;
if(hasMouseCapture() )
{
if( mIsSelecting )
{
- if (x != mLastSelectionX || y != mLastSelectionY)
- {
- mLastSelectionX = x;
- mLastSelectionY = y;
- }
-
- if( y > mTextRect.mTop )
- {
- mScrollbar->setDocPos( mScrollbar->getDocPos() - 1 );
+ if(mScroller)
+ {
+ mScroller->autoScroll(x, y);
}
- else
- if( y < mTextRect.mBottom )
- {
- mScrollbar->setDocPos( mScrollbar->getDocPos() + 1 );
- }
-
- setCursorAtLocalPos( x, y, TRUE );
+ S32 clamped_x = llclamp(x, mVisibleTextRect.mLeft, mVisibleTextRect.mRight);
+ S32 clamped_y = llclamp(y, mVisibleTextRect.mBottom, mVisibleTextRect.mTop);
+ setCursorAtLocalPos( clamped_x, clamped_y, true );
mSelectionEnd = mCursorPos;
}
-
lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (active)" << llendl;
getWindow()->setCursor(UI_CURSOR_IBEAM);
handled = TRUE;
@@ -1227,61 +771,21 @@ BOOL LLTextEditor::handleHover(S32 x, S32 y, MASK mask)
if( !handled )
{
// Pass to children
- handled = LLView::childrenHandleHover(x, y, mask) != NULL;
+ handled = LLTextBase::handleHover(x, y, mask);
}
if( handled )
{
// Delay cursor flashing
- resetKeystrokeTimer();
+ resetCursorBlink();
}
- // Opaque
- if( !handled && mTakesNonScrollClicks)
+ if( !handled )
{
- // Check to see if we're over an HTML-style link
- if( !mSegments.empty() )
- {
- const LLTextSegment* cur_segment = getSegmentAtLocalPos( x, y );
- if( cur_segment )
- {
- if(cur_segment->getStyle()->isLink())
- {
- lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (over link, inactive)" << llendl;
- getWindow()->setCursor(UI_CURSOR_HAND);
- handled = TRUE;
- }
- else
- if(cur_segment->getStyle()->getIsEmbeddedItem())
- {
- lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (over embedded item, inactive)" << llendl;
- getWindow()->setCursor(UI_CURSOR_HAND);
- //getWindow()->setCursor(UI_CURSOR_ARROW);
- handled = TRUE;
- }
- mHoverSegment = cur_segment;
- }
- }
-
- if( !handled )
- {
- lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (inactive)" << llendl;
- if (!mScrollbar->getVisible() || x < getRect().getWidth() - scrollbar_size)
- {
- getWindow()->setCursor(UI_CURSOR_IBEAM);
- }
- else
- {
- getWindow()->setCursor(UI_CURSOR_ARROW);
- }
- handled = TRUE;
- }
+ getWindow()->setCursor(UI_CURSOR_IBEAM);
+ handled = TRUE;
}
- if (mOnScrollEndCallback && mOnScrollEndData && (mScrollbar->getDocPos() == mScrollbar->getDocPosMax()))
- {
- mOnScrollEndCallback(mOnScrollEndData);
- }
return handled;
}
@@ -1290,33 +794,27 @@ BOOL LLTextEditor::handleMouseUp(S32 x, S32 y, MASK mask)
{
BOOL handled = FALSE;
- // let scrollbar have first dibs
- handled = LLView::childrenHandleMouseUp(x, y, mask) != NULL;
+ // if I'm not currently selecting text
+ if (!(hasSelection() && hasMouseCapture()))
+ {
+ // let text segments handle mouse event
+ handled = LLTextBase::handleMouseUp(x, y, mask);
+ }
- if( !handled && mTakesNonScrollClicks)
+ if( !handled )
{
if( mIsSelecting )
{
- // Finish selection
- if( y > mTextRect.mTop )
+ if(mScroller)
{
- mScrollbar->setDocPos( mScrollbar->getDocPos() - 1 );
+ mScroller->autoScroll(x, y);
}
- else
- if( y < mTextRect.mBottom )
- {
- mScrollbar->setDocPos( mScrollbar->getDocPos() + 1 );
- }
-
- setCursorAtLocalPos( x, y, TRUE );
+ S32 clamped_x = llclamp(x, mVisibleTextRect.mLeft, mVisibleTextRect.mRight);
+ S32 clamped_y = llclamp(y, mVisibleTextRect.mBottom, mVisibleTextRect.mTop);
+ setCursorAtLocalPos( clamped_x, clamped_y, true );
endSelection();
}
- if( !hasSelection() )
- {
- handleMouseUpOverSegment( x, y, mask );
- }
-
// take selection to 'primary' clipboard
updatePrimary();
@@ -1324,7 +822,7 @@ BOOL LLTextEditor::handleMouseUp(S32 x, S32 y, MASK mask)
}
// Delay cursor flashing
- resetKeystrokeTimer();
+ resetCursorBlink();
if( hasMouseCapture() )
{
@@ -1341,28 +839,28 @@ BOOL LLTextEditor::handleDoubleClick(S32 x, S32 y, MASK mask)
{
BOOL handled = FALSE;
- // let scrollbar have first dibs
- handled = LLView::childrenHandleDoubleClick(x, y, mask) != NULL;
+ // let scrollbar and text segments have first dibs
+ handled = LLTextBase::handleDoubleClick(x, y, mask);
- if( !handled && mTakesNonScrollClicks)
+ if( !handled )
{
- setCursorAtLocalPos( x, y, FALSE );
+ setCursorAtLocalPos( x, y, false );
deselect();
LLWString text = getWText();
- if( isPartOfWord( text[mCursorPos] ) )
+ if( LLWStringUtil::isPartOfWord( text[mCursorPos] ) )
{
// Select word the cursor is over
- while ((mCursorPos > 0) && isPartOfWord(text[mCursorPos-1]))
+ while ((mCursorPos > 0) && LLWStringUtil::isPartOfWord(text[mCursorPos-1]))
{
- mCursorPos--;
+ if (!setCursorPos(mCursorPos - 1)) break;
}
startSelection();
- while ((mCursorPos < (S32)text.length()) && isPartOfWord( text[mCursorPos] ) )
+ while ((mCursorPos < (S32)text.length()) && LLWStringUtil::isPartOfWord( text[mCursorPos] ) )
{
- mCursorPos++;
+ if (!setCursorPos(mCursorPos + 1)) break;
}
mSelectionEnd = mCursorPos;
@@ -1371,7 +869,7 @@ BOOL LLTextEditor::handleDoubleClick(S32 x, S32 y, MASK mask)
{
// Select the character the cursor is over
startSelection();
- mCursorPos++;
+ setCursorPos(mCursorPos + 1);
mSelectionEnd = mCursorPos;
}
@@ -1380,7 +878,7 @@ BOOL LLTextEditor::handleDoubleClick(S32 x, S32 y, MASK mask)
mIsSelecting = FALSE;
// delay cursor flashing
- resetKeystrokeTimer();
+ resetCursorBlink();
// take selection to 'primary' clipboard
updatePrimary();
@@ -1392,38 +890,36 @@ BOOL LLTextEditor::handleDoubleClick(S32 x, S32 y, MASK mask)
}
-// Allow calling cards to be dropped onto text fields. Append the name and
-// a carriage return.
-// virtual
-BOOL LLTextEditor::handleDragAndDrop(S32 x, S32 y, MASK mask,
- BOOL drop, EDragAndDropType cargo_type, void *cargo_data,
- EAcceptance *accept,
- std::string& tooltip_msg)
-{
- *accept = ACCEPT_NO;
-
- return TRUE;
-}
-
//----------------------------------------------------------------------------
// Returns change in number of characters in mText
-S32 LLTextEditor::execute( LLTextCmd* cmd )
+S32 LLTextEditor::execute( TextCmd* cmd )
{
S32 delta = 0;
if( cmd->execute(this, &delta) )
{
// Delete top of undo stack
undo_stack_t::iterator enditer = std::find(mUndoStack.begin(), mUndoStack.end(), mLastCmd);
- if (enditer != mUndoStack.begin())
- {
- --enditer;
- std::for_each(mUndoStack.begin(), enditer, DeletePointer());
- mUndoStack.erase(mUndoStack.begin(), enditer);
- }
+ std::for_each(mUndoStack.begin(), enditer, DeletePointer());
+ mUndoStack.erase(mUndoStack.begin(), enditer);
// Push the new command is now on the top (front) of the undo stack.
mUndoStack.push_front(cmd);
mLastCmd = cmd;
+
+ bool need_to_rollback = 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
{
@@ -1434,19 +930,20 @@ S32 LLTextEditor::execute( LLTextCmd* cmd )
return delta;
}
-S32 LLTextEditor::insert(const S32 pos, const LLWString &wstr, const BOOL group_with_next_op)
+S32 LLTextEditor::insert(S32 pos, const LLWString &wstr, bool group_with_next_op, LLTextSegmentPtr segment)
{
- return execute( new LLTextCmdInsert( pos, group_with_next_op, wstr ) );
+ return execute( new TextCmdInsert( pos, group_with_next_op, wstr, segment ) );
}
-S32 LLTextEditor::remove(const S32 pos, const S32 length, const BOOL group_with_next_op)
+S32 LLTextEditor::remove(S32 pos, S32 length, bool group_with_next_op)
{
- return execute( new LLTextCmdRemove( pos, group_with_next_op, length ) );
-}
+ S32 end_pos = getEditableIndex(pos + length, true);
-S32 LLTextEditor::append(const LLWString &wstr, const BOOL group_with_next_op)
-{
- return insert(getLength(), wstr, group_with_next_op);
+ segment_vec_t segments_to_remove;
+ // store text segments
+ getSegmentsInRange(segments_to_remove, pos, pos + length, false);
+
+ return execute( new TextCmdRemove( pos, group_with_next_op, end_pos - pos, segments_to_remove ) );
}
S32 LLTextEditor::overwriteChar(S32 pos, llwchar wc)
@@ -1457,7 +954,7 @@ S32 LLTextEditor::overwriteChar(S32 pos, llwchar wc)
}
else
{
- return execute(new LLTextCmdOverwriteChar(pos, FALSE, wc));
+ return execute(new TextCmdOverwriteChar(pos, FALSE, wc));
}
}
@@ -1477,8 +974,7 @@ void LLTextEditor::removeCharOrTab()
if (text[mCursorPos - 1] == ' ')
{
// Try to remove a "tab"
- S32 line, offset;
- getLineAndOffset(mCursorPos, &line, &offset);
+ S32 offset = getLineOffsetFromDocIndex(mCursorPos);
if (offset > 0)
{
chars_to_remove = offset % SPACES_PER_TAB;
@@ -1508,7 +1004,7 @@ void LLTextEditor::removeCharOrTab()
}
else
{
- reportBadKeystroke();
+ LLUI::reportBadKeystroke();
}
}
@@ -1531,14 +1027,14 @@ void LLTextEditor::removeChar()
}
else
{
- reportBadKeystroke();
+ LLUI::reportBadKeystroke();
}
}
// Add a single character to the text
S32 LLTextEditor::addChar(S32 pos, llwchar wc)
{
- if ( (wstring_utf8_length( getWText() ) + wchar_utf8_length( wc )) >= mMaxTextByteLength)
+ if ( (wstring_utf8_length( getWText() ) + wchar_utf8_length( wc )) > mMaxTextByteLength)
{
make_ui_sound("UISndBadKeystroke");
return 0;
@@ -1547,12 +1043,26 @@ 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
{
- return execute(new LLTextCmdAddChar(pos, FALSE, wc));
+ return execute(new TextCmdAddChar(pos, FALSE, wc, LLTextSegmentPtr()));
}
}
@@ -1573,6 +1083,28 @@ void LLTextEditor::addChar(llwchar wc)
setCursorPos(mCursorPos + addChar( mCursorPos, wc ));
}
+void LLTextEditor::addLineBreakChar()
+{
+ if( !getEnabled() )
+ {
+ return;
+ }
+ if( hasSelection() )
+ {
+ deleteSelection(TRUE);
+ }
+ else if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode())
+ {
+ removeChar(mCursorPos);
+ }
+
+ LLStyleConstSP sp(new LLStyle(LLStyle::Params()));
+ LLTextSegmentPtr segment = new LLLineBreakTextSegment(sp, mCursorPos);
+
+ S32 pos = execute(new TextCmdAddChar(mCursorPos, FALSE, '\n', segment));
+
+ setCursorPos(mCursorPos + pos);
+}
BOOL LLTextEditor::handleSelectionKey(const KEY key, const MASK mask)
@@ -1589,10 +1121,10 @@ BOOL LLTextEditor::handleSelectionKey(const KEY key, const MASK mask)
if( 0 < mCursorPos )
{
startSelection();
- mCursorPos--;
+ setCursorPos(mCursorPos - 1);
if( mask & MASK_CONTROL )
{
- mCursorPos = prevWordPos(mCursorPos);
+ setCursorPos(prevWordPos(mCursorPos));
}
mSelectionEnd = mCursorPos;
}
@@ -1602,10 +1134,10 @@ BOOL LLTextEditor::handleSelectionKey(const KEY key, const MASK mask)
if( mCursorPos < getLength() )
{
startSelection();
- mCursorPos++;
+ setCursorPos(mCursorPos + 1);
if( mask & MASK_CONTROL )
{
- mCursorPos = nextWordPos(mCursorPos);
+ setCursorPos(nextWordPos(mCursorPos));
}
mSelectionEnd = mCursorPos;
}
@@ -1627,7 +1159,7 @@ BOOL LLTextEditor::handleSelectionKey(const KEY key, const MASK mask)
startSelection();
if( mask & MASK_CONTROL )
{
- mCursorPos = 0;
+ setCursorPos(0);
}
else
{
@@ -1652,7 +1184,7 @@ BOOL LLTextEditor::handleSelectionKey(const KEY key, const MASK mask)
startSelection();
if( mask & MASK_CONTROL )
{
- mCursorPos = getLength();
+ setCursorPos(getLength());
}
else
{
@@ -1667,22 +1199,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
@@ -1703,14 +1219,7 @@ BOOL LLTextEditor::handleNavigationKey(const KEY key, const MASK mask)
switch( key )
{
case KEY_UP:
- if (mReadOnly)
- {
- mScrollbar->setDocPos(mScrollbar->getDocPos() - 1);
- }
- else
- {
- changeLine( -1 );
- }
+ changeLine( -1 );
break;
case KEY_PAGE_UP:
@@ -1718,25 +1227,12 @@ BOOL LLTextEditor::handleNavigationKey(const KEY key, const MASK mask)
break;
case KEY_HOME:
- if (mReadOnly)
- {
- mScrollbar->setDocPos(0);
- }
- else
- {
- startOfLine();
- }
+ startOfLine();
break;
case KEY_DOWN:
- if (mReadOnly)
- {
- mScrollbar->setDocPos(mScrollbar->getDocPos() + 1);
- }
- else
- {
- changeLine( 1 );
- }
+ changeLine( 1 );
+ deselect();
break;
case KEY_PAGE_DOWN:
@@ -1744,24 +1240,13 @@ BOOL LLTextEditor::handleNavigationKey(const KEY key, const MASK mask)
break;
case KEY_END:
- if (mReadOnly)
- {
- mScrollbar->setDocPos(mScrollbar->getDocPosMax());
- }
- else
- {
- endOfLine();
- }
+ endOfLine();
break;
case KEY_LEFT:
- if (mReadOnly)
- {
- break;
- }
if( hasSelection() )
{
- setCursorPos(llmin( mCursorPos - 1, mSelectionStart, mSelectionEnd ));
+ setCursorPos(llmin( mSelectionStart, mSelectionEnd ));
}
else
{
@@ -1771,19 +1256,15 @@ BOOL LLTextEditor::handleNavigationKey(const KEY key, const MASK mask)
}
else
{
- reportBadKeystroke();
+ LLUI::reportBadKeystroke();
}
}
break;
case KEY_RIGHT:
- if (mReadOnly)
- {
- break;
- }
if( hasSelection() )
{
- setCursorPos(llmax( mCursorPos + 1, mSelectionStart, mSelectionEnd ));
+ setCursorPos(llmax( mSelectionStart, mSelectionEnd ));
}
else
{
@@ -1793,7 +1274,7 @@ BOOL LLTextEditor::handleNavigationKey(const KEY key, const MASK mask)
}
else
{
- reportBadKeystroke();
+ LLUI::reportBadKeystroke();
}
}
break;
@@ -1804,10 +1285,11 @@ BOOL LLTextEditor::handleNavigationKey(const KEY key, const MASK mask)
}
}
- if (mOnScrollEndCallback && mOnScrollEndData && (mScrollbar->getDocPos() == mScrollbar->getDocPosMax()))
+ if (handled)
{
- mOnScrollEndCallback(mOnScrollEndData);
+ deselect();
}
+
return handled;
}
@@ -1843,7 +1325,7 @@ void LLTextEditor::cut()
gClipboard.copyFromSubstring( getWText(), left_pos, length, mSourceID );
deleteSelection( FALSE );
- needsReflow();
+ onKeyStroke();
}
BOOL LLTextEditor::canCopy() const
@@ -1932,7 +1414,7 @@ void LLTextEditor::pasteHelper(bool is_primary)
for( S32 i = 0; i < len; i++ )
{
llwchar wc = clean_string[i];
- if( (wc < LLFont::FIRST_CHAR) && (wc != LF) )
+ if( (wc < LLFontFreetype::FIRST_CHAR) && (wc != LF) )
{
clean_string[i] = LL_UNKNOWN_CHAR;
}
@@ -1944,10 +1426,30 @@ void LLTextEditor::pasteHelper(bool is_primary)
}
// Insert the new text into the existing text.
- setCursorPos(mCursorPos + insert(mCursorPos, clean_string, FALSE));
+
+ //paste text with linebreaks.
+ std::basic_string<llwchar>::size_type start = 0;
+ std::basic_string<llwchar>::size_type pos = clean_string.find('\n',start);
+
+ while(pos!=-1)
+ {
+ if(pos!=start)
+ {
+ std::basic_string<llwchar> str = std::basic_string<llwchar>(clean_string,start,pos-start);
+ setCursorPos(mCursorPos + insert(mCursorPos, str, FALSE, LLTextSegmentPtr()));
+ }
+ addLineBreakChar();
+
+ start = pos+1;
+ pos = clean_string.find('\n',start);
+ }
+
+ std::basic_string<llwchar> str = std::basic_string<llwchar>(clean_string,start,clean_string.length()-start);
+ setCursorPos(mCursorPos + insert(mCursorPos, str, FALSE, LLTextSegmentPtr()));
+
deselect();
- needsReflow();
+ onKeyStroke();
}
@@ -1991,7 +1493,7 @@ BOOL LLTextEditor::handleControlKey(const KEY key, const MASK mask)
if( mask & MASK_SHIFT )
{
startSelection();
- mCursorPos = 0;
+ setCursorPos(0);
mSelectionEnd = mCursorPos;
}
else
@@ -1999,7 +1501,7 @@ BOOL LLTextEditor::handleControlKey(const KEY key, const MASK mask)
// Ctrl-Home, Ctrl-Left, Ctrl-Right, Ctrl-Down
// all move the cursor as if clicking, so should deselect.
deselect();
- setCursorPos(0);
+ startOfDoc();
}
break;
@@ -2060,75 +1562,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 )
- {
- if( canDoDelete() )
- {
- doDelete();
- }
- else
- {
- reportBadKeystroke();
- }
- handled = TRUE;
- }
- else
- if( MASK_CONTROL & mask )
+BOOL LLTextEditor::handleSpecialKey(const KEY key, const MASK 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:
@@ -2150,7 +1590,7 @@ BOOL LLTextEditor::handleSpecialKey(const KEY key, const MASK mask, BOOL* return
}
else
{
- reportBadKeystroke();
+ LLUI::reportBadKeystroke();
}
break;
@@ -2188,8 +1628,7 @@ BOOL LLTextEditor::handleSpecialKey(const KEY key, const MASK mask, BOOL* return
deleteSelection(FALSE);
}
- S32 line, offset;
- getLineAndOffset( mCursorPos, &line, &offset );
+ S32 offset = getLineOffsetFromDocIndex(mCursorPos);
S32 spaces_needed = SPACES_PER_TAB - (offset % SPACES_PER_TAB);
for( S32 i=0; i < spaces_needed; i++ )
@@ -2204,6 +1643,10 @@ BOOL LLTextEditor::handleSpecialKey(const KEY key, const MASK mask, BOOL* return
break;
}
+ if (handled)
+ {
+ onKeyStroke();
+ }
return handled;
}
@@ -2224,88 +1667,32 @@ 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;
- if ( gFocusMgr.getKeyboardFocus() == this )
+ // Special case for TAB. If want to move to next field, report
+ // not handled and let the parent take care of field movement.
+ if (KEY_TAB == key && mTabsToNextField)
{
- // Special case for TAB. If want to move to next field, report
- // not handled and let the parent take care of field movement.
- if (KEY_TAB == key && mTabsToNextField)
- {
- return FALSE;
- }
-
- 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;
- }
- }
-
+ return FALSE;
+ }
+
+ if (mReadOnly && mScroller)
+ {
+ handled = (mScroller && mScroller->handleKeyHere( key, mask ))
+ || handleSelectionKey(key, mask)
+ || handleControlKey(key, mask);
}
-
- if( handled )
+ else
{
- resetKeystrokeTimer();
-
- // 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();
- }
+ handled = handleNavigationKey( key, mask )
+ || handleSelectionKey(key, mask)
+ || handleControlKey(key, mask)
+ || handleSpecialKey(key, mask);
+ }
- if(text_may_have_changed)
- {
- needsReflow();
- }
- needsScroll();
- }
+ if( handled )
+ {
+ resetCursorBlink();
+ needsScroll();
}
return handled;
@@ -2321,34 +1708,31 @@ BOOL LLTextEditor::handleUnicodeCharHere(llwchar uni_char)
BOOL handled = FALSE;
- if ( gFocusMgr.getKeyboardFocus() == this )
+ // Handle most keys only if the text editor is writeable.
+ if( !mReadOnly )
{
- // Handle most keys only if the text editor is writeable.
- if( !mReadOnly )
+ if( '}' == uni_char )
{
- if( '}' == uni_char )
- {
- unindentLineBeforeCloseBrace();
- }
+ unindentLineBeforeCloseBrace();
+ }
- // TODO: KLW Add auto show of tool tip on (
- addChar( uni_char );
+ // TODO: KLW Add auto show of tool tip on (
+ addChar( uni_char );
- // Keys that add characters temporarily hide the cursor
- getWindow()->hideCursorUntilMouseMove();
+ // Keys that add characters temporarily hide the cursor
+ getWindow()->hideCursorUntilMouseMove();
- handled = TRUE;
- }
+ handled = TRUE;
+ }
- if( handled )
- {
- resetKeystrokeTimer();
+ if( handled )
+ {
+ resetCursorBlink();
- // Most keystrokes will make the selection box go away, but not all will.
- deselect();
+ // Most keystrokes will make the selection box go away, but not all will.
+ deselect();
- needsReflow();
- }
+ onKeyStroke();
}
return handled;
@@ -2380,8 +1764,7 @@ void LLTextEditor::doDelete()
if( (text[ mCursorPos ] == ' ') && (mCursorPos + SPACES_PER_TAB < getLength()) )
{
// Try to remove a full tab's worth of spaces
- S32 line, offset;
- getLineAndOffset( mCursorPos, &line, &offset );
+ S32 offset = getLineOffsetFromDocIndex(mCursorPos);
chars_to_remove = SPACES_PER_TAB - (offset % SPACES_PER_TAB);
if( chars_to_remove == 0 )
{
@@ -2403,9 +1786,10 @@ void LLTextEditor::doDelete()
setCursorPos(mCursorPos + 1);
removeChar();
}
+
}
- needsReflow();
+ onKeyStroke();
}
//----------------------------------------------------------------------------
@@ -2448,7 +1832,7 @@ void LLTextEditor::undo()
setCursorPos(pos);
- needsReflow();
+ onKeyStroke();
}
BOOL LLTextEditor::canRedo() const
@@ -2490,12 +1874,12 @@ void LLTextEditor::redo()
setCursorPos(pos);
- needsReflow();
+ onKeyStroke();
}
void LLTextEditor::onFocusReceived()
{
- LLUICtrl::onFocusReceived();
+ LLTextBase::onFocusReceived();
updateAllowingLanguageInput();
}
@@ -2518,323 +1902,57 @@ void LLTextEditor::onFocusLost()
// Make sure cursor is shown again
getWindow()->showCursorFromMouseMove();
- LLUICtrl::onFocusLost();
+ LLTextBase::onFocusLost();
+}
+
+void LLTextEditor::onCommit()
+{
+ setControlValue(getValue());
+ LLTextBase::onCommit();
}
void LLTextEditor::setEnabled(BOOL enabled)
{
// just treat enabled as read-only flag
- BOOL read_only = !enabled;
+ bool read_only = !enabled;
if (read_only != mReadOnly)
{
- mReadOnly = read_only;
+ //mReadOnly = read_only;
+ LLTextBase::setReadOnly(read_only);
updateSegments();
updateAllowingLanguageInput();
}
}
-void LLTextEditor::drawBackground()
-{
- S32 left = 0;
- S32 top = getRect().getHeight();
- S32 right = getRect().getWidth();
- S32 bottom = 0;
-
- LLColor4 bg_color = mReadOnly ? mReadOnlyBgColor.get()
- : gFocusMgr.getKeyboardFocus() == this ? mFocusBgColor.get() : mWriteableBgColor.get();
- if( mShowLineNumbers ) {
- gl_rect_2d(left, top, UI_TEXTEDITOR_LINE_NUMBER_MARGIN, bottom, mReadOnlyBgColor.get() ); // line number area always read-only
- gl_rect_2d(UI_TEXTEDITOR_LINE_NUMBER_MARGIN, top, right, bottom, bg_color); // body text area to the right of line numbers
- gl_rect_2d(UI_TEXTEDITOR_LINE_NUMBER_MARGIN, top, UI_TEXTEDITOR_LINE_NUMBER_MARGIN-1, bottom, LLColor4::grey3); // separator
- } else {
- gl_rect_2d(left, top, right, bottom, bg_color); // body text area
- }
-
- LLView::draw();
-}
-
-// Draws the black box behind the selected text
-void LLTextEditor::drawSelectionBackground()
+void LLTextEditor::showContextMenu(S32 x, S32 y)
{
- // Draw selection even if we don't have keyboard focus for search/replace
- if( hasSelection() )
+ if (!mContextMenu)
{
- LLWString text = getWText();
- const S32 text_len = getLength();
- std::queue<S32> line_endings;
-
- S32 line_height = llround( mGLFont->getLineHeight() );
-
- S32 selection_left = llmin( mSelectionStart, mSelectionEnd );
- S32 selection_right = llmax( mSelectionStart, mSelectionEnd );
- S32 selection_left_x = mTextRect.mLeft;
- S32 selection_left_y = mTextRect.mTop - line_height;
- S32 selection_right_x = mTextRect.mRight;
- S32 selection_right_y = mTextRect.mBottom;
-
- BOOL selection_left_visible = FALSE;
- BOOL selection_right_visible = FALSE;
-
- // Skip through the lines we aren't drawing.
- S32 cur_line = mScrollbar->getDocPos();
-
- S32 left_line_num = cur_line;
- S32 num_lines = getLineCount();
- S32 right_line_num = num_lines - 1;
-
- S32 line_start = -1;
- if (cur_line >= num_lines)
- {
- return;
- }
-
- line_start = getLineStart(cur_line);
-
- S32 left_visible_pos = line_start;
- S32 right_visible_pos = line_start;
-
- S32 text_y = mTextRect.mTop - line_height;
-
- // Find the coordinates of the selected area
- while((cur_line < num_lines))
- {
- S32 next_line = -1;
- S32 line_end = text_len;
-
- if ((cur_line + 1) < num_lines)
- {
- next_line = getLineStart(cur_line + 1);
- line_end = next_line;
-
- line_end = ( (line_end - line_start)==0 || text[next_line-1] == '\n' || text[next_line-1] == '\0' || text[next_line-1] == ' ' || text[next_line-1] == '\t' ) ? next_line-1 : next_line;
- }
-
- const llwchar* line = text.c_str() + line_start;
-
- if( line_start <= selection_left && selection_left <= line_end )
- {
- left_line_num = cur_line;
- selection_left_visible = TRUE;
- selection_left_x = mTextRect.mLeft + mGLFont->getWidth(line, 0, selection_left - line_start, mAllowEmbeddedItems);
- selection_left_y = text_y;
- }
- if( line_start <= selection_right && selection_right <= line_end )
- {
- right_line_num = cur_line;
- selection_right_visible = TRUE;
- selection_right_x = mTextRect.mLeft + mGLFont->getWidth(line, 0, selection_right - line_start, mAllowEmbeddedItems);
- if (selection_right == line_end)
- {
- // add empty space for "newline"
- //selection_right_x += mGLFont->getWidth("n");
- }
- selection_right_y = text_y;
- }
-
- // if selection spans end of current line...
- if (selection_left <= line_end && line_end < selection_right && selection_left != selection_right)
- {
- // extend selection slightly beyond end of line
- // to indicate selection of newline character (use "n" character to determine width)
- const LLWString nstr(utf8str_to_wstring(std::string("n")));
- line_endings.push(mTextRect.mLeft + mGLFont->getWidth(line, 0, line_end - line_start, mAllowEmbeddedItems) + mGLFont->getWidth(nstr.c_str()));
- }
-
- // move down one line
- text_y -= line_height;
-
- right_visible_pos = line_end;
- line_start = next_line;
- cur_line++;
-
- if (selection_right_visible)
- {
- break;
- }
- }
-
- // Draw the selection box (we're using a box instead of reversing the colors on the selected text).
- BOOL selection_visible = (left_visible_pos <= selection_right) && (selection_left <= right_visible_pos);
- if( selection_visible )
- {
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
- const LLColor4& color = mReadOnly ? mReadOnlyBgColor.get() : mWriteableBgColor.get();
- F32 alpha = hasFocus() ? 1.f : 0.5f;
- gGL.color4f( 1.f - color.mV[0], 1.f - color.mV[1], 1.f - color.mV[2], alpha );
- S32 margin_offset = mShowLineNumbers ? UI_TEXTEDITOR_LINE_NUMBER_MARGIN : 0;
-
- if( selection_left_y == selection_right_y )
- {
- // Draw from selection start to selection end
- gl_rect_2d( selection_left_x + margin_offset, selection_left_y + line_height + 1,
- selection_right_x + margin_offset, selection_right_y);
- }
- else
- {
- // Draw from selection start to the end of the first line
- if( mTextRect.mRight == selection_left_x )
- {
- selection_left_x -= CURSOR_THICKNESS;
- }
-
- S32 line_end = line_endings.front();
- line_endings.pop();
- gl_rect_2d( selection_left_x + margin_offset, selection_left_y + line_height + 1,
- line_end + margin_offset, selection_left_y );
-
- S32 line_num = left_line_num + 1;
- while(line_endings.size())
- {
- S32 vert_offset = -(line_num - left_line_num) * line_height;
- // Draw the block between the two lines
- gl_rect_2d( mTextRect.mLeft + margin_offset, selection_left_y + vert_offset + line_height + 1,
- line_endings.front() + margin_offset, selection_left_y + vert_offset);
- line_endings.pop();
- line_num++;
- }
-
- // Draw from the start of the last line to selection end
- if( mTextRect.mLeft == selection_right_x )
- {
- selection_right_x += CURSOR_THICKNESS;
- }
- gl_rect_2d( mTextRect.mLeft + margin_offset, selection_right_y + line_height + 1,
- selection_right_x + margin_offset, selection_right_y );
- }
- }
+ mContextMenu = LLUICtrlFactory::instance().createFromFile<LLContextMenu>("menu_text_editor.xml",
+ LLMenuGL::sMenuContainer,
+ LLMenuHolderGL::child_registry_t::instance());
}
-}
-
-void LLTextEditor::drawCursor()
-{
- if( gFocusMgr.getKeyboardFocus() == this
- && gShowTextEditCursor && !mReadOnly)
- {
- LLWString text = getWText();
- const S32 text_len = getLength();
-
- // Skip through the lines we aren't drawing.
- S32 cur_pos = mScrollbar->getDocPos();
- S32 num_lines = getLineCount();
- if (cur_pos >= num_lines)
- {
- return;
- }
- S32 line_start = getLineStart(cur_pos);
-
- F32 line_height = mGLFont->getLineHeight();
- F32 text_y = (F32)(mTextRect.mTop) - line_height;
-
- F32 cursor_left = 0.f;
- F32 next_char_left = 0.f;
- F32 cursor_bottom = 0.f;
- BOOL cursor_visible = FALSE;
-
- S32 line_end = 0;
- // Determine if the cursor is visible and if so what its coordinates are.
- while( (mTextRect.mBottom <= llround(text_y)) && (cur_pos < num_lines))
- {
- line_end = text_len + 1;
- S32 next_line = -1;
-
- if ((cur_pos + 1) < num_lines)
- {
- next_line = getLineStart(cur_pos + 1);
- line_end = next_line - 1;
- }
-
- const llwchar* line = text.c_str() + line_start;
-
- // Find the cursor and selection bounds
- if( line_start <= mCursorPos && mCursorPos <= line_end )
- {
- cursor_visible = TRUE;
- next_char_left = (F32)mTextRect.mLeft + mGLFont->getWidthF32(line, 0, mCursorPos - line_start, mAllowEmbeddedItems );
- cursor_left = next_char_left - 1.f;
- cursor_bottom = text_y;
- break;
- }
-
- // move down one line
- text_y -= line_height;
- line_start = next_line;
- cur_pos++;
- }
-
- if(mShowLineNumbers)
- {
- cursor_left += UI_TEXTEDITOR_LINE_NUMBER_MARGIN;
- }
-
- // Draw the cursor
- if( cursor_visible )
- {
- // (Flash the cursor every half second starting a fixed time after the last keystroke)
- F32 elapsed = mKeystrokeTimer.getElapsedTimeF32();
- if( (elapsed < CURSOR_FLASH_DELAY ) || (S32(elapsed * 2) & 1) )
- {
- F32 cursor_top = cursor_bottom + line_height + 1.f;
- F32 cursor_right = cursor_left + (F32)CURSOR_THICKNESS;
- if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode() && !hasSelection())
- {
- cursor_left += CURSOR_THICKNESS;
- const LLWString space(utf8str_to_wstring(std::string(" ")));
- F32 spacew = mGLFont->getWidthF32(space.c_str());
- if (mCursorPos == line_end)
- {
- cursor_right = cursor_left + spacew;
- }
- else
- {
- F32 width = mGLFont->getWidthF32(text.c_str(), mCursorPos, 1, mAllowEmbeddedItems);
- cursor_right = cursor_left + llmax(spacew, width);
- }
- }
-
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+ // 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)
- gGL.color4fv( mCursorColor.get().mV );
-
- gl_rect_2d(llfloor(cursor_left), llfloor(cursor_top),
- llfloor(cursor_right), llfloor(cursor_bottom));
+ gEditMenuHandler = this;
- if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode() && !hasSelection() && text[mCursorPos] != '\n')
- {
- const LLTextSegment* segmentp = getSegmentAtOffset(mCursorPos);
- LLColor4 text_color;
- if (segmentp)
- {
- text_color = segmentp->getColor();
- }
- else if (mReadOnly)
- {
- text_color = mReadOnlyFgColor.get();
- }
- else
- {
- text_color = mFgColor.get();
- }
- mGLFont->render(text, mCursorPos, next_char_left, cursor_bottom + line_height,
- LLColor4(1.f - text_color.mV[VRED], 1.f - text_color.mV[VGREEN], 1.f - text_color.mV[VBLUE], 1.f),
- LLFontGL::LEFT, LLFontGL::TOP,
- LLFontGL::NORMAL,
- LLFontGL::NO_SHADOW,
- 1);
- }
-
- // Make sure the IME is in the right place
- LLRect screen_pos = calcScreenRect();
- LLCoordGL ime_pos( screen_pos.mLeft + llfloor(cursor_left), screen_pos.mBottom + llfloor(cursor_top) );
-
- ime_pos.mX = (S32) (ime_pos.mX * LLUI::sGLScaleFactor.mV[VX]);
- ime_pos.mY = (S32) (ime_pos.mY * LLUI::sGLScaleFactor.mV[VY]);
- getWindow()->setLanguageTextInput( ime_pos );
- }
- }
- }
+ S32 screen_x, screen_y;
+ localPointToScreen(x, y, &screen_x, &screen_y);
+ mContextMenu->show(screen_x, screen_y);
}
+
void LLTextEditor::drawPreeditMarker()
{
static LLUICachedControl<F32> preedit_marker_brightness ("UIPreeditMarkerBrightness", 0);
@@ -2856,17 +1974,17 @@ void LLTextEditor::drawPreeditMarker()
const S32 text_len = getLength();
const S32 num_lines = getLineCount();
- S32 cur_line = mScrollbar->getDocPos();
+ S32 cur_line = getFirstVisibleLine();
if (cur_line >= num_lines)
{
return;
}
- const S32 line_height = llround( mGLFont->getLineHeight() );
+ const S32 line_height = llround( mDefaultFont->getLineHeight() );
S32 line_start = getLineStart(cur_line);
- S32 line_y = mTextRect.mTop - line_height;
- while((mTextRect.mBottom <= line_y) && (num_lines > cur_line))
+ S32 line_y = mVisibleTextRect.mTop - line_height;
+ while((mVisibleTextRect.mBottom <= line_y) && (num_lines > cur_line))
{
S32 next_start = -1;
S32 line_end = text_len;
@@ -2898,19 +2016,19 @@ void LLTextEditor::drawPreeditMarker()
continue;
}
- S32 preedit_left = mTextRect.mLeft;
+ S32 preedit_left = mVisibleTextRect.mLeft;
if (left > line_start)
{
- preedit_left += mGLFont->getWidth(text, line_start, left - line_start, mAllowEmbeddedItems);
+ preedit_left += mDefaultFont->getWidth(text, line_start, left - line_start);
}
- S32 preedit_right = mTextRect.mLeft;
+ S32 preedit_right = mVisibleTextRect.mLeft;
if (right < line_end)
{
- preedit_right += mGLFont->getWidth(text, line_start, right - line_start, mAllowEmbeddedItems);
+ preedit_right += mDefaultFont->getWidth(text, line_start, right - line_start);
}
else
{
- preedit_right += mGLFont->getWidth(text, line_start, line_end - line_start, mAllowEmbeddedItems);
+ preedit_right += mDefaultFont->getWidth(text, line_start, line_end - line_start);
}
if (mPreeditStandouts[i])
@@ -2940,251 +2058,85 @@ void LLTextEditor::drawPreeditMarker()
}
-void LLTextEditor::drawText()
+void LLTextEditor::drawLineNumbers()
{
- LLWString text = getWText();
- const S32 text_len = getLength();
- if( text_len <= 0 )
- {
- return;
- }
- S32 selection_left = -1;
- S32 selection_right = -1;
- // Draw selection even if we don't have keyboard focus for search/replace
- if( hasSelection())
- {
- selection_left = llmin( mSelectionStart, mSelectionEnd );
- selection_right = llmax( mSelectionStart, mSelectionEnd );
- }
-
LLGLSUIDefault gls_ui;
-
- S32 cur_line = mScrollbar->getDocPos();
+ LLRect scrolled_view_rect = getVisibleDocumentRect();
+ LLRect content_rect = getVisibleTextRect();
+ LLLocalClipRect clip(content_rect);
+ S32 first_line = getFirstVisibleLine();
S32 num_lines = getLineCount();
- if (cur_line >= num_lines)
+ if (first_line >= num_lines)
{
return;
}
- S32 line_start = getLineStart(cur_line);
- LLTextSegment t(line_start);
- segment_list_t::iterator seg_iter;
- seg_iter = std::upper_bound(mSegments.begin(), mSegments.end(), &t, LLTextSegment::compare());
- if (seg_iter == mSegments.end() || (*seg_iter)->getStart() > line_start) --seg_iter;
- LLTextSegment* cur_segment = *seg_iter;
-
- S32 line_height = llround( mGLFont->getLineHeight() );
- F32 text_y = (F32)(mTextRect.mTop - line_height);
- while((mTextRect.mBottom <= text_y) && (cur_line < num_lines))
+ S32 cursor_line = mLineInfoList[getLineNumFromDocIndex(mCursorPos)].mLineNum;
+
+ if (mShowLineNumbers)
{
- S32 next_start = -1;
- S32 line_end = text_len;
+ S32 left = 0;
+ S32 top = getRect().getHeight();
+ S32 bottom = 0;
- if ((cur_line + 1) < num_lines)
- {
- next_start = getLineStart(cur_line + 1);
- line_end = next_start;
- }
- if ( text[line_end-1] == '\n' )
- {
- --line_end;
- }
-
- F32 text_x = (F32)mTextRect.mLeft;
+ gl_rect_2d(left, top, UI_TEXTEDITOR_LINE_NUMBER_MARGIN, bottom, mReadOnlyBgColor.get() ); // line number area always read-only
+ gl_rect_2d(UI_TEXTEDITOR_LINE_NUMBER_MARGIN, top, UI_TEXTEDITOR_LINE_NUMBER_MARGIN-1, bottom, LLColor4::grey3); // separator
+
+ S32 last_line_num = -1;
- S32 seg_start = line_start;
- while( seg_start < line_end )
+ for (S32 cur_line = first_line; cur_line < num_lines; cur_line++)
{
- while( cur_segment->getEnd() <= seg_start )
+ line_info& line = mLineInfoList[cur_line];
+
+ if ((line.mRect.mTop - scrolled_view_rect.mBottom) < mVisibleTextRect.mBottom)
{
- seg_iter++;
- if (seg_iter == mSegments.end())
- {
- llwarns << "Ran off the segmentation end!" << llendl;
- return;
- }
- cur_segment = *seg_iter;
+ break;
}
-
- // Draw a segment within the line
- S32 clipped_end = llmin( line_end, cur_segment->getEnd() );
- S32 clipped_len = clipped_end - seg_start;
- if( clipped_len > 0 )
- {
- LLStyleSP style = cur_segment->getStyle();
- if ( style->isImage() && (cur_segment->getStart() >= seg_start) && (cur_segment->getStart() <= clipped_end))
- {
- S32 style_image_height = style->mImageHeight;
- S32 style_image_width = style->mImageWidth;
- LLUIImagePtr image = style->getImage();
- image->draw(llround(text_x), llround(text_y)+line_height-style_image_height,
- style_image_width, style_image_height);
- }
-
- if (cur_segment == mHoverSegment && style->getIsEmbeddedItem())
- {
- style->mUnderline = TRUE;
- }
-
- S32 left_pos = llmin( mSelectionStart, mSelectionEnd );
-
- if ( (mParseHTML) && (left_pos > seg_start) && (left_pos < clipped_end) && mIsSelecting && (mSelectionStart == mSelectionEnd) )
- {
- mHTML = style->getLinkHREF();
- }
- drawClippedSegment( text, seg_start, clipped_end, text_x, text_y, selection_left, selection_right, style, &text_x );
-
- // Note: text_x is incremented by drawClippedSegment()
- seg_start += clipped_len;
+ S32 line_bottom = line.mRect.mBottom - scrolled_view_rect.mBottom + mVisibleTextRect.mBottom;
+ // draw the line numbers
+ if(line.mLineNum != last_line_num && line.mRect.mTop <= scrolled_view_rect.mTop)
+ {
+ const LLFontGL *num_font = LLFontGL::getFontMonospace();
+ const LLWString ltext = utf8str_to_wstring(llformat("%d", line.mLineNum ));
+ BOOL is_cur_line = cursor_line == line.mLineNum;
+ const U8 style = is_cur_line ? LLFontGL::BOLD : LLFontGL::NORMAL;
+ const LLColor4 fg_color = is_cur_line ? mCursorColor : mReadOnlyFgColor;
+ num_font->render(
+ ltext, // string to draw
+ 0, // begin offset
+ UI_TEXTEDITOR_LINE_NUMBER_MARGIN - 2, // x
+ line_bottom, // y
+ fg_color,
+ LLFontGL::RIGHT, // horizontal alignment
+ LLFontGL::BOTTOM, // vertical alignment
+ style,
+ LLFontGL::NO_SHADOW,
+ S32_MAX, // max chars
+ UI_TEXTEDITOR_LINE_NUMBER_MARGIN - 2); // max pixels
+ last_line_num = line.mLineNum;
}
}
-
- // move down one line
- text_y -= (F32)line_height;
-
- line_start = next_start;
- cur_line++;
}
}
-
-// Draws a single text segment, reversing the color for selection if needed.
-void LLTextEditor::drawClippedSegment(const LLWString &text, S32 seg_start, S32 seg_end, F32 x, F32 y, S32 selection_left, S32 selection_right, const LLStyleSP& style, F32* right_x )
-{
- if (!style->isVisible())
- {
- return;
- }
-
- const LLFontGL* font = mGLFont;
-
- LLColor4 color = style->getColor();
-
- if ( style->getFontString()[0] )
- {
- font = style->getFont();
- }
-
- U8 font_flags = LLFontGL::NORMAL;
-
- if (style->mBold)
- {
- font_flags |= LLFontGL::BOLD;
- }
- if (style->mItalic)
- {
- font_flags |= LLFontGL::ITALIC;
- }
- if (style->mUnderline)
- {
- font_flags |= LLFontGL::UNDERLINE;
- }
-
- if (style->getIsEmbeddedItem())
- {
- static LLUICachedControl<LLColor4> text_embedded_item_readonly_color ("TextEmbeddedItemReadOnlyColor", *(new LLColor4));
- static LLUICachedControl<LLColor4> text_embedded_item_color ("TextEmbeddedItemColor", *(new LLColor4));
- if (mReadOnly)
- {
- color = text_embedded_item_readonly_color;
- }
- else
- {
- color = text_embedded_item_color;
- }
- }
-
- F32 y_top = y + (F32)llround(font->getLineHeight());
-
- if( selection_left > seg_start )
- {
- // Draw normally
- S32 start = seg_start;
- S32 end = llmin( selection_left, seg_end );
- S32 length = end - start;
- font->render(text, start, x, y_top, color, LLFontGL::LEFT, LLFontGL::TOP, mGLFontStyle, LLFontGL::NO_SHADOW, length, S32_MAX, right_x, mAllowEmbeddedItems);
- }
- x = *right_x;
-
- if( (selection_left < seg_end) && (selection_right > seg_start) )
- {
- // Draw reversed
- S32 start = llmax( selection_left, seg_start );
- S32 end = llmin( selection_right, seg_end );
- S32 length = end - start;
-
- font->render(text, start, x, y_top,
- LLColor4( 1.f - color.mV[0], 1.f - color.mV[1], 1.f - color.mV[2], 1.f ),
- LLFontGL::LEFT, LLFontGL::TOP, mGLFontStyle, LLFontGL::NO_SHADOW, length, S32_MAX, right_x, mAllowEmbeddedItems);
- }
- x = *right_x;
- if( selection_right < seg_end )
- {
- // Draw normally
- S32 start = llmax( selection_right, seg_start );
- S32 end = seg_end;
- S32 length = end - start;
- font->render(text, start, x, y_top, color, LLFontGL::LEFT, LLFontGL::TOP, mGLFontStyle, LLFontGL::NO_SHADOW, length, S32_MAX, right_x, mAllowEmbeddedItems);
- }
- }
-
-
void LLTextEditor::draw()
{
- // do on-demand reflow
- if (mReflowNeeded)
{
- updateLineStartList();
- mReflowNeeded = FALSE;
- }
-
- // then update scroll position, as cursor may have moved
- if (mScrollNeeded)
- {
- updateScrollFromCursor();
- mScrollNeeded = FALSE;
- }
-
- {
- static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
- LLLocalClipRect clip(LLRect(0, getRect().getHeight(), getRect().getWidth() - (mScrollbar->getVisible() ? scrollbar_size : 0), 0));
-
- bindEmbeddedChars( mGLFont );
-
- drawBackground();
- drawSelectionBackground();
+ // pad clipping rectangle so that cursor can draw at full width
+ // when at left edge of mVisibleTextRect
+ LLRect clip_rect(mVisibleTextRect);
+ clip_rect.stretch(1);
+ LLLocalClipRect clip(clip_rect);
drawPreeditMarker();
- drawText();
- drawCursor();
-
- unbindEmbeddedChars( mGLFont );
-
- //RN: the decision was made to always show the orange border for keyboard focus but do not put an insertion caret
- // when in readonly mode
- mBorder->setKeyboardFocusHighlight( gFocusMgr.getKeyboardFocus() == this);// && !mReadOnly);
}
-
- LLView::draw(); // Draw children (scrollbar and border)
-
- // remember if we are supposed to be at the bottom of the buffer
- mScrolledToBottom = isScrolledToBottom();
-}
+ LLTextBase::draw();
+ drawLineNumbers();
-void LLTextEditor::onTabInto()
-{
- // selecting all on tabInto causes users to hit tab twice and replace their text with a tab character
- // theoretically, one could selectAll if mTabsToNextField is true, but we couldn't think of a use case
- // where you'd want to select all anyway
- // preserve insertion point when returning to the editor
- //selectAll();
-}
-
-// virtual
-void LLTextEditor::clear()
-{
- setText(LLStringUtil::null);
+ //RN: the decision was made to always show the orange border for keyboard focus but do not put an insertion caret
+ // when in readonly mode
+ mBorder->setKeyboardFocusHighlight( hasFocus() );// && !mReadOnly);
}
// Start or stop the editor from accepting text-editing keystrokes
@@ -3202,7 +2154,7 @@ void LLTextEditor::setFocus( BOOL new_state )
getWindow()->allowLanguageTextInput(this, FALSE);
}
- LLUICtrl::setFocus( new_state );
+ LLTextBase::setFocus( new_state );
if( new_state )
{
@@ -3210,7 +2162,7 @@ void LLTextEditor::setFocus( BOOL new_state )
gEditMenuHandler = this;
// Don't start the cursor flashing right away
- resetKeystrokeTimer();
+ resetCursorBlink();
}
else
{
@@ -3224,286 +2176,23 @@ void LLTextEditor::setFocus( BOOL new_state )
}
}
-// virtual
-BOOL LLTextEditor::acceptsTextInput() const
-{
- return !mReadOnly;
-}
-
-// Given a line (from the start of the doc) and an offset into the line, find the offset (pos) into text.
-S32 LLTextEditor::getPos( S32 line, S32 offset )
-{
- S32 line_start = getLineStart(line);
- S32 next_start = getLineStart(line+1);
- if (next_start == line_start)
- {
- next_start = getLength() + 1;
- }
- S32 line_length = next_start - line_start - 1;
- line_length = llmax(line_length, 0);
- return line_start + llmin( offset, line_length );
-}
-
-
-void LLTextEditor::changePage( S32 delta )
-{
- S32 line, offset;
- getLineAndOffset( mCursorPos, &line, &offset );
-
- // get desired x position to remember previous position
- S32 desired_x_pixel = mDesiredXPixel;
-
- // allow one line overlap
- S32 page_size = mScrollbar->getPageSize() - 1;
- if( delta == -1 )
- {
- line = llmax( line - page_size, 0);
- setCursorPos(getPos( line, offset ));
- mScrollbar->setDocPos( mScrollbar->getDocPos() - page_size );
- }
- else
- if( delta == 1 )
- {
- setCursorPos(getPos( line + page_size, offset ));
- mScrollbar->setDocPos( mScrollbar->getDocPos() + page_size );
- }
-
- // put desired position into remember-buffer after setCursorPos()
- mDesiredXPixel = desired_x_pixel;
-
- if (mOnScrollEndCallback && mOnScrollEndData && (mScrollbar->getDocPos() == mScrollbar->getDocPosMax()))
- {
- mOnScrollEndCallback(mOnScrollEndData);
- }
-}
-
-void LLTextEditor::changeLine( S32 delta )
-{
- bindEmbeddedChars(mGLFont);
-
- S32 line, offset;
- getLineAndOffset( mCursorPos, &line, &offset );
-
- S32 line_start = getLineStart(line);
-
- // set desired x position to remembered previous position
- S32 desired_x_pixel = mDesiredXPixel;
- // if remembered position was reset (thus -1), calculate new one here
- if( desired_x_pixel == -1 )
- {
- LLWString text(getWText());
- desired_x_pixel = mGLFont->getWidth(text.c_str(), line_start, offset, mAllowEmbeddedItems );
- }
-
- S32 new_line = 0;
- if( (delta < 0) && (line > 0 ) )
- {
- new_line = line - 1;
- }
- else
- if( (delta > 0) && (line < (getLineCount() - 1)) )
- {
- new_line = line + 1;
- }
- else
- {
- unbindEmbeddedChars(mGLFont);
- return;
- }
-
- S32 num_lines = getLineCount();
- S32 new_line_start = getLineStart(new_line);
- S32 new_line_end = getLength();
- if (new_line + 1 < num_lines)
- {
- new_line_end = getLineStart(new_line + 1) - 1;
- }
-
- S32 new_line_len = new_line_end - new_line_start;
-
- S32 new_offset;
- LLWString text(getWText());
- new_offset = mGLFont->charFromPixelOffset(text.c_str(), new_line_start,
- (F32)desired_x_pixel,
- (F32)mTextRect.getWidth(),
- new_line_len,
- mAllowEmbeddedItems);
-
- setCursorPos (getPos( new_line, new_offset ));
-
- // put desired position into remember-buffer after setCursorPos()
- mDesiredXPixel = desired_x_pixel;
- unbindEmbeddedChars(mGLFont);
-}
-
-BOOL LLTextEditor::isScrolledToTop()
-{
- return mScrollbar->isAtBeginning();
-}
-
-BOOL LLTextEditor::isScrolledToBottom()
-{
- return mScrollbar->isAtEnd();
-}
-
-
-void LLTextEditor::startOfLine()
-{
- S32 line, offset;
- getLineAndOffset( mCursorPos, &line, &offset );
- setCursorPos(mCursorPos - offset);
-}
-
-
// public
void LLTextEditor::setCursorAndScrollToEnd()
{
deselect();
endOfDoc();
- needsScroll();
-}
-
-void LLTextEditor::getLineAndColumnForPosition( S32 position, S32* line, S32* col, BOOL include_wordwrap )
-{
- if( include_wordwrap )
- {
- getLineAndOffset( mCursorPos, line, col );
- }
- else
- {
- LLWString text = getWText();
- S32 line_count = 0;
- S32 line_start = 0;
- S32 i;
- for( i = 0; text[i] && (i < position); i++ )
- {
- if( '\n' == text[i] )
- {
- line_start = i + 1;
- line_count++;
- }
- }
- *line = line_count;
- *col = i - line_start;
- }
}
void LLTextEditor::getCurrentLineAndColumn( S32* line, S32* col, BOOL include_wordwrap )
{
- getLineAndColumnForPosition(mCursorPos, line, col, include_wordwrap);
-}
-
-S32 LLTextEditor::getCurrentLine()
-{
- return getLineForPosition(mCursorPos);
-}
-
-S32 LLTextEditor::getLineForPosition(S32 position)
-{
- S32 line, col;
- getLineAndColumnForPosition(position, &line, &col, FALSE);
- return line;
-}
-
-
-void LLTextEditor::endOfLine()
-{
- S32 line, offset;
- getLineAndOffset( mCursorPos, &line, &offset );
- S32 num_lines = getLineCount();
- if (line + 1 >= num_lines)
- {
- setCursorPos(getLength());
- }
- else
- {
- setCursorPos( getLineStart(line + 1) - 1 );
- }
-}
-
-void LLTextEditor::endOfDoc()
-{
- mScrollbar->setDocPos(mScrollbar->getDocPosMax());
- mScrolledToBottom = true;
-
- S32 len = getLength();
- if( len )
- {
- setCursorPos(len);
- }
- if (mOnScrollEndCallback && mOnScrollEndData && (mScrollbar->getDocPos() == mScrollbar->getDocPosMax()))
- {
- mOnScrollEndCallback(mOnScrollEndData);
- }
-}
-
-// Sets the scrollbar from the cursor position
-void LLTextEditor::updateScrollFromCursor()
-{
- mScrollbar->setDocSize( getLineCount() );
-
- if (mReadOnly)
- {
- // no cursor in read only mode
- return;
- }
-
- S32 line, offset;
- getLineAndOffset( mCursorPos, &line, &offset );
-
- S32 page_size = mScrollbar->getPageSize();
-
- if( line < mScrollbar->getDocPos() )
- {
- // scroll so that the cursor is at the top of the page
- mScrollbar->setDocPos( line );
- }
- else if( line >= mScrollbar->getDocPos() + page_size - 1 )
- {
- S32 new_pos = 0;
- if( line < mScrollbar->getDocSize() - 1 )
- {
- // scroll so that the cursor is one line above the bottom of the page,
- new_pos = line - page_size + 1;
- }
- else
- {
- // if there is less than a page of text remaining, scroll so that the cursor is at the bottom
- new_pos = mScrollbar->getDocPosMax();
- }
- mScrollbar->setDocPos( new_pos );
- }
-
- // Check if we've scrolled to bottom for callback if asked for callback
- if (mOnScrollEndCallback && mOnScrollEndData && (mScrollbar->getDocPos() == mScrollbar->getDocPosMax()))
- {
- mOnScrollEndCallback(mOnScrollEndData);
- }
-}
-
-void LLTextEditor::reshape(S32 width, S32 height, BOOL called_from_parent)
-{
- LLView::reshape( width, height, called_from_parent );
-
- // do this first after reshape, because other things depend on
- // up-to-date mTextRect
- updateTextRect();
-
- needsReflow();
-
- // propagate shape information to scrollbar
- mScrollbar->setDocSize( getLineCount() );
-
- S32 line_height = llround( mGLFont->getLineHeight() );
- S32 page_lines = mTextRect.getHeight() / line_height;
- mScrollbar->setPageSize( page_lines );
+ *line = getLineNumFromDocIndex(mCursorPos, include_wordwrap);
+ *col = getLineOffsetFromDocIndex(mCursorPos, include_wordwrap);
}
void LLTextEditor::autoIndent()
{
// Count the number of spaces in the current line
- S32 line, offset;
- getLineAndOffset( mCursorPos, &line, &offset );
+ S32 line = getLineNumFromDocIndex(mCursorPos, false);
S32 line_start = getLineStart(line);
S32 space_count = 0;
S32 i;
@@ -3522,7 +2211,10 @@ void LLTextEditor::autoIndent()
}
// Insert that number of spaces on the new line
- addChar( '\n' );
+
+ //appendLineBreakSegment(LLStyle::Params());//addChar( '\n' );
+ addLineBreakChar();
+
for( i = 0; i < space_count; i++ )
{
addChar( ' ' );
@@ -3541,129 +2233,14 @@ void LLTextEditor::insertText(const std::string &new_text)
deleteSelection(TRUE);
}
- setCursorPos(mCursorPos + insert( mCursorPos, utf8str_to_wstring(new_text), FALSE ));
+ setCursorPos(mCursorPos + insert( mCursorPos, utf8str_to_wstring(new_text), FALSE, LLTextSegmentPtr() ));
- needsReflow();
-
setEnabled( enabled );
}
-
-void LLTextEditor::appendColoredText(const std::string &new_text,
- bool allow_undo,
- bool prepend_newline,
- const LLColor4 &color,
- const std::string& font_name)
-{
- LLColor4 lcolor=color;
- if (mParseHighlights)
- {
- LLTextParser* highlight = LLTextParser::getInstance();
- highlight->parseFullLineHighlights(new_text, &lcolor);
- }
-
- LLStyleSP style(new LLStyle);
- style->setVisible(true);
- style->setColor(lcolor);
- style->setFontName(font_name);
- appendStyledText(new_text, allow_undo, prepend_newline, style);
-}
-
-void LLTextEditor::appendStyledText(const std::string &new_text,
- bool allow_undo,
- bool prepend_newline,
- LLStyleSP stylep)
-{
- S32 part = (S32)LLTextParser::WHOLE;
- if(mParseHTML)
- {
-
- S32 start=0,end=0;
- std::string text = new_text;
- while ( findHTML(text, &start, &end) )
- {
- LLStyleSP html(new LLStyle);
- html->setVisible(true);
- html->setColor(mLinkColor);
- if (stylep)
- {
- html->setFontName(stylep->getFontString());
- }
- html->mUnderline = TRUE;
-
- if (start > 0)
- {
- if (part == (S32)LLTextParser::WHOLE ||
- part == (S32)LLTextParser::START)
- {
- part = (S32)LLTextParser::START;
- }
- else
- {
- part = (S32)LLTextParser::MIDDLE;
- }
- std::string subtext=text.substr(0,start);
- appendHighlightedText(subtext,allow_undo, prepend_newline, part, stylep);
- }
-
- html->setLinkHREF(text.substr(start,end-start));
- appendText(text.substr(start, end-start),allow_undo, prepend_newline, html);
- if (end < (S32)text.length())
- {
- text = text.substr(end,text.length() - end);
- end=0;
- part=(S32)LLTextParser::END;
- }
- else
- {
- break;
- }
- }
- if (part != (S32)LLTextParser::WHOLE) part=(S32)LLTextParser::END;
- if (end < (S32)text.length()) appendHighlightedText(text,allow_undo, prepend_newline, part, stylep);
- }
- else
- {
- appendHighlightedText(new_text, allow_undo, prepend_newline, part, stylep);
- }
-}
-
-void LLTextEditor::appendHighlightedText(const std::string &new_text,
- bool allow_undo,
- bool prepend_newline,
- S32 highlight_part,
- LLStyleSP stylep)
-{
- if (mParseHighlights)
- {
- LLTextParser* highlight = LLTextParser::getInstance();
-
- if (highlight && stylep)
- {
- LLSD pieces = highlight->parsePartialLineHighlights(new_text, stylep->getColor(), highlight_part);
- bool lprepend=prepend_newline;
- for (S32 i=0;i<pieces.size();i++)
- {
- LLSD color_llsd = pieces[i]["color"];
- LLColor4 lcolor;
- lcolor.setValue(color_llsd);
- LLStyleSP lstylep(new LLStyle(*stylep));
- lstylep->setColor(lcolor);
- if (i != 0 && (pieces.size() > 1) ) lprepend=FALSE;
- appendText((std::string)pieces[i]["text"], allow_undo, lprepend, lstylep);
- }
- return;
- }
- }
- appendText(new_text, allow_undo, prepend_newline, stylep);
-}
-
-// Appends new text to end of document
-void LLTextEditor::appendText(const std::string &new_text, bool allow_undo, bool prepend_newline,
- const LLStyleSP stylep)
+void LLTextEditor::appendWidget(const LLInlineViewSegment::Params& params, const std::string& text, bool allow_undo)
{
// Save old state
- BOOL was_scrolled_to_bottom = (mScrollbar->getDocPos() == mScrollbar->getDocPosMax());
S32 selection_start = mSelectionStart;
S32 selection_end = mSelectionEnd;
BOOL was_selecting = mIsSelecting;
@@ -3675,50 +2252,17 @@ void LLTextEditor::appendText(const std::string &new_text, bool allow_undo, bool
setCursorPos(old_length);
- // Add carriage return if not first line
- if (getLength() != 0
- && prepend_newline)
- {
- std::string final_text = "\n";
- final_text += new_text;
- append(utf8str_to_wstring(final_text), TRUE);
- }
- else
- {
- append(utf8str_to_wstring(new_text), TRUE );
- }
+ LLWString widget_wide_text = utf8str_to_wstring(text);
+
+ LLTextSegmentPtr segment = new LLInlineViewSegment(params, old_length, old_length + widget_wide_text.size());
+ insert(getLength(), widget_wide_text, FALSE, segment);
- if (stylep)
- {
- S32 segment_start = old_length;
- S32 segment_end = getLength();
- LLTextSegment* segment = new LLTextSegment(stylep, segment_start, segment_end );
- mSegments.push_back(segment);
- }
-
- needsReflow();
-
// Set the cursor and scroll position
- // Maintain the scroll position unless the scroll was at the end of the doc (in which
- // case, move it to the new end of the doc) or unless the user was doing actively selecting
- if( was_scrolled_to_bottom && !was_selecting )
- {
- if( selection_start != selection_end )
- {
- // maintain an existing non-active selection
- mSelectionStart = selection_start;
- mSelectionEnd = selection_end;
- }
- endOfDoc();
- }
- else if( selection_start != selection_end )
+ if( selection_start != selection_end )
{
mSelectionStart = selection_start;
mSelectionEnd = selection_end;
-
-
-
mIsSelecting = was_selecting;
setCursorPos(cursor_pos);
}
@@ -3731,7 +2275,7 @@ void LLTextEditor::appendText(const std::string &new_text, bool allow_undo, bool
setCursorPos(cursor_pos);
}
- if( !allow_undo )
+ if (!allow_undo)
{
blockUndo();
}
@@ -3744,59 +2288,13 @@ void LLTextEditor::removeTextFromEnd(S32 num_chars)
remove(getLength() - num_chars, num_chars, FALSE);
S32 len = getLength();
- mCursorPos = llclamp(mCursorPos, 0, len);
+ setCursorPos (llclamp(mCursorPos, 0, len));
mSelectionStart = llclamp(mSelectionStart, 0, len);
mSelectionEnd = llclamp(mSelectionEnd, 0, len);
- pruneSegments();
-
- // pruneSegments will invalidate mLineStartList.
- updateLineStartList();
needsScroll();
}
-///////////////////////////////////////////////////////////////////
-// Returns change in number of characters in mWText
-
-S32 LLTextEditor::insertStringNoUndo(const S32 pos, const LLWString &wstr)
-{
- LLWString text(getWText());
- S32 old_len = text.length(); // length() returns character length
- S32 insert_len = wstr.length();
-
- text.insert(pos, wstr);
- getViewModel()->setDisplay(text);
-
- if ( truncate() )
- {
- // The user's not getting everything he's hoping for
- make_ui_sound("UISndBadKeystroke");
- insert_len = getLength() - old_len;
- }
-
- return insert_len;
-}
-
-S32 LLTextEditor::removeStringNoUndo(S32 pos, S32 length)
-{
- LLWString text(getWText());
- text.erase(pos, length);
- getViewModel()->setDisplay(text);
- return -length; // This will be wrong if someone calls removeStringNoUndo with an excessive length
-}
-
-S32 LLTextEditor::overwriteCharNoUndo(S32 pos, llwchar wc)
-{
- if (pos > (S32)getLength())
- {
- return 0;
- }
- LLWString text(getWText());
- text[pos] = wc;
- getViewModel()->setDisplay(text);
- return 1;
-}
-
//----------------------------------------------------------------------------
void LLTextEditor::makePristine()
@@ -3852,33 +2350,19 @@ BOOL LLTextEditor::tryToRevertToPristineState()
i--;
}
}
-
- needsReflow();
}
return isPristine(); // TRUE => success
}
-void LLTextEditor::updateTextRect()
-{
- static LLUICachedControl<S32> texteditor_border ("UITextEditorBorder", 0);
- static LLUICachedControl<S32> texteditor_h_pad ("UITextEditorHPad", 0);
- static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
- static LLUICachedControl<S32> texteditor_vpad_top ("UITextEditorVPadTop", 0);
-
- mTextRect.setOriginAndSize(
- texteditor_border + texteditor_h_pad,
- texteditor_border,
- getRect().getWidth() - scrollbar_size - 2 * (texteditor_border + texteditor_h_pad),
- getRect().getHeight() - 2 * texteditor_border - texteditor_vpad_top );
-}
-
+static LLFastTimer::DeclareTimer FTM_SYNTAX_HIGHLIGHTING("Syntax Highlighting");
void LLTextEditor::loadKeywords(const std::string& filename,
const std::vector<std::string>& funcs,
const std::vector<std::string>& tooltips,
const LLColor3& color)
{
+ LLFastTimer ft(FTM_SYNTAX_HIGHLIGHTING);
if(mKeywords.loadFromFile(filename))
{
S32 count = llmin(funcs.size(), tooltips.size());
@@ -3887,199 +2371,73 @@ void LLTextEditor::loadKeywords(const std::string& filename,
std::string name = utf8str_trim(funcs[i]);
mKeywords.addToken(LLKeywordToken::WORD, name, color, tooltips[i] );
}
+ segment_vec_t segment_list;
+ mKeywords.findSegments(&segment_list, getWText(), mDefaultColor.get(), *this);
- mKeywords.findSegments( &mSegments, getWText(), mDefaultColor.get() );
-
- llassert( mSegments.front()->getStart() == 0 );
- llassert( mSegments.back()->getEnd() == getLength() );
+ mSegments.clear();
+ segment_set_t::iterator insert_it = mSegments.begin();
+ for (segment_vec_t::iterator list_it = segment_list.begin(); list_it != segment_list.end(); ++list_it)
+ {
+ insert_it = mSegments.insert(insert_it, *list_it);
+ }
}
}
void LLTextEditor::updateSegments()
{
- if (mKeywords.isLoaded())
+ if (mReflowIndex < S32_MAX && mKeywords.isLoaded())
{
+ LLFastTimer ft(FTM_SYNTAX_HIGHLIGHTING);
// HACK: No non-ascii keywords for now
- mKeywords.findSegments(&mSegments, getWText(), mDefaultColor.get());
- }
- else if (mAllowEmbeddedItems)
- {
- findEmbeddedItemSegments();
- }
-
- // Make sure we have at least one segment
- if (mSegments.size() == 1 && mSegments[0]->getIsDefault())
- {
- delete mSegments[0];
- mSegments.clear(); // create default segment
- }
- if (mSegments.empty())
- {
- LLColor4 text_color = ( mReadOnly ? mReadOnlyFgColor.get() : mFgColor.get() );
- LLTextSegment* default_segment = new LLTextSegment( text_color, 0, getLength() );
- default_segment->setIsDefault(TRUE);
- mSegments.push_back(default_segment);
- }
-}
+ segment_vec_t segment_list;
+ mKeywords.findSegments(&segment_list, getWText(), mDefaultColor.get(), *this);
-// Only effective if text was removed from the end of the editor
-// *NOTE: Using this will invalidate references to mSegments from mLineStartList.
-void LLTextEditor::pruneSegments()
-{
- S32 len = getLength();
- // Find and update the first valid segment
- segment_list_t::iterator iter = mSegments.end();
- while(iter != mSegments.begin())
- {
- --iter;
- LLTextSegment* seg = *iter;
- if (seg->getStart() < len)
+ clearSegments();
+ segment_set_t::iterator insert_it = mSegments.begin();
+ for (segment_vec_t::iterator list_it = segment_list.begin(); list_it != segment_list.end(); ++list_it)
{
- // valid segment
- if (seg->getEnd() > len)
- {
- seg->setEnd(len);
- }
- break; // done
- }
- }
- if (iter != mSegments.end())
- {
- // erase invalid segments
- ++iter;
- std::for_each(iter, mSegments.end(), DeletePointer());
- mSegments.erase(iter, mSegments.end());
- }
- else
- {
- llwarns << "Tried to erase end of empty LLTextEditor" << llendl;
- }
-}
-
-void LLTextEditor::findEmbeddedItemSegments()
-{
- mHoverSegment = NULL;
- std::for_each(mSegments.begin(), mSegments.end(), DeletePointer());
- mSegments.clear();
-
- BOOL found_embedded_items = FALSE;
- LLWString text = getWText();
- S32 idx = 0;
- while( text[idx] )
- {
- if( text[idx] >= FIRST_EMBEDDED_CHAR && text[idx] <= LAST_EMBEDDED_CHAR )
- {
- found_embedded_items = TRUE;
- break;
+ insertSegment(*list_it);
}
- ++idx;
}
- if( !found_embedded_items )
- {
- return;
- }
-
- S32 text_len = text.length();
-
- BOOL in_text = FALSE;
-
- LLColor4 text_color = ( mReadOnly ? mReadOnlyFgColor.get() : mFgColor.get() );
-
- if( idx > 0 )
- {
- mSegments.push_back( new LLTextSegment( text_color, 0, text_len ) ); // text
- in_text = TRUE;
- }
-
- LLStyleSP embedded_style(new LLStyle);
- embedded_style->setIsEmbeddedItem( TRUE );
-
- // Start with i just after the first embedded item
- while ( text[idx] )
- {
- if( text[idx] >= FIRST_EMBEDDED_CHAR && text[idx] <= LAST_EMBEDDED_CHAR )
- {
- if( in_text )
- {
- mSegments.back()->setEnd( idx );
- }
- mSegments.push_back( new LLTextSegment( embedded_style, idx, idx + 1 ) ); // item
- in_text = FALSE;
- }
- else
- if( !in_text )
- {
- mSegments.push_back( new LLTextSegment( text_color, idx, text_len ) ); // text
- in_text = TRUE;
- }
- ++idx;
- }
+ LLTextBase::updateSegments();
}
-BOOL LLTextEditor::handleMouseUpOverSegment(S32 x, S32 y, MASK mask)
+void LLTextEditor::updateLinkSegments()
{
- if ( hasMouseCapture() )
+ LLWString wtext = getWText();
+
+ // update any segments that contain a link
+ for (segment_set_t::iterator it = mSegments.begin(); it != mSegments.end(); ++it)
{
- // This mouse up was part of a click.
- // Regardless of where the cursor is, see if we recently touched a link
- // and launch it if we did.
- if (mParseHTML && mHTML.length() > 0)
+ LLTextSegment *segment = *it;
+ if (segment && segment->getStyle() && segment->getStyle()->isLink())
{
- //Special handling for slurls
- if ( (mSecondlifeURLcallback!=NULL) && !(*mSecondlifeURLcallback)(mHTML) )
+ // 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.
+ 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))
{
- if (mURLcallback!=NULL) (*mURLcallback)(mHTML);
+ std::string new_url = wstring_to_utf8str(url_label);
+ LLStringUtil::trim(new_url);
+ new_style->setLinkHREF(new_url);
+ LLStyleConstSP sp(new_style);
+ segment->setStyle(sp);
}
- mHTML.clear();
}
}
-
- return FALSE;
}
-// Finds the text segment (if any) at the give local screen position
-const LLTextSegment* LLTextEditor::getSegmentAtLocalPos( S32 x, S32 y ) const
-{
- // Find the cursor position at the requested local screen position
- S32 offset = getCursorPosFromLocalCoord( x, y, FALSE );
- S32 idx = getSegmentIdxAtOffset(offset);
- return idx >= 0 ? mSegments[idx] : NULL;
-}
-
-const LLTextSegment* LLTextEditor::getSegmentAtOffset(S32 offset) const
-{
- S32 idx = getSegmentIdxAtOffset(offset);
- return idx >= 0 ? mSegments[idx] : NULL;
-}
-
-S32 LLTextEditor::getSegmentIdxAtOffset(S32 offset) const
-{
- if (mSegments.empty() || offset < 0 || offset >= getLength())
- {
- return -1;
- }
- else
- {
- S32 segidx, segoff;
- getSegmentAndOffset(offset, &segidx, &segoff);
- return segidx;
- }
-}
void LLTextEditor::onMouseCaptureLost()
{
endSelection();
}
-void LLTextEditor::setOnScrollEndCallback(void (*callback)(void*), void* userdata)
-{
- mOnScrollEndCallback = callback;
- mOnScrollEndData = userdata;
- mScrollbar->setOnScrollEndCallback(callback, userdata);
-}
-
///////////////////////////////////////////////////////////////////
// Hack for Notecards
@@ -4163,10 +2521,9 @@ BOOL LLTextEditor::importBuffer(const char* buffer, S32 length )
delete[] text;
- setCursorPos(0);
+ startOfDoc();
deselect();
- needsReflow();
return success;
}
@@ -4184,237 +2541,6 @@ BOOL LLTextEditor::exportBuffer(std::string &buffer )
return TRUE;
}
-//////////////////////////////////////////////////////////////////////////
-// LLTextSegment
-
-LLTextSegment::LLTextSegment(S32 start) :
- mStart(start),
- mEnd(0),
- mToken(NULL),
- mIsDefault(FALSE)
-{
-}
-LLTextSegment::LLTextSegment( const LLStyleSP& style, S32 start, S32 end ) :
- mStyle( style ),
- mStart( start),
- mEnd( end ),
- mToken(NULL),
- mIsDefault(FALSE)
-{
-}
-LLTextSegment::LLTextSegment( const LLColor4& color, S32 start, S32 end, BOOL is_visible) :
- mStyle(new LLStyle(is_visible,color,LLStringUtil::null)),
- mStart( start),
- mEnd( end ),
- mToken(NULL),
- mIsDefault(FALSE)
-{
-}
-LLTextSegment::LLTextSegment( const LLColor4& color, S32 start, S32 end ) :
- mStyle(new LLStyle(TRUE, color,LLStringUtil::null )),
- mStart( start),
- mEnd( end ),
- mToken(NULL),
- mIsDefault(FALSE)
-{
-}
-LLTextSegment::LLTextSegment( const LLColor3& color, S32 start, S32 end ) :
- mStyle(new LLStyle(TRUE, color,LLStringUtil::null )),
- mStart( start),
- mEnd( end ),
- mToken(NULL),
- mIsDefault(FALSE)
-{
-}
-
-BOOL LLTextSegment::getToolTip(std::string& msg) const
-{
- if (mToken && !mToken->getToolTip().empty())
- {
- const LLWString& wmsg = mToken->getToolTip();
- msg = wstring_to_utf8str(wmsg);
- return TRUE;
- }
- return FALSE;
-}
-
-
-
-void LLTextSegment::dump() const
-{
- llinfos << "Segment [" <<
-// mColor.mV[VX] << ", " <<
-// mColor.mV[VY] << ", " <<
-// mColor.mV[VZ] << "]\t[" <<
- mStart << ", " <<
- getEnd() << "]" <<
- llendl;
-
-}
-
-///////////////////////////////////////////////////////////////////
-// Refactoring note: We may eventually want to replace this with boost::regex or
-// boost::tokenizer capabilities since we've already fixed at least two JIRAs
-// concerning logic issues associated with this function.
-S32 LLTextEditor::findHTMLToken(const std::string &line, S32 pos, BOOL reverse) const
-{
- std::string openers=" \t\n('\"[{<>";
- std::string closers=" \t\n)'\"]}><;";
-
- if (reverse)
- {
- for (int index=pos; index >= 0; index--)
- {
- char c = line[index];
- S32 m2 = openers.find(c);
- if (m2 >= 0)
- {
- return index+1;
- }
- }
- return 0; // index is -1, don't want to return that.
- }
- else
- {
- // adjust the search slightly, to allow matching parenthesis inside the URL
- S32 paren_count = 0;
- for (int index=pos; index<(S32)line.length(); index++)
- {
- char c = line[index];
-
- if (c == '(')
- {
- paren_count++;
- }
- else if (c == ')')
- {
- if (paren_count <= 0)
- {
- return index;
- }
- else
- {
- paren_count--;
- }
- }
- else
- {
- S32 m2 = closers.find(c);
- if (m2 >= 0)
- {
- return index;
- }
- }
- }
- return line.length();
- }
-}
-
-BOOL LLTextEditor::findHTML(const std::string &line, S32 *begin, S32 *end) const
-{
-
- S32 m1,m2,m3;
- BOOL matched = FALSE;
-
- m1=line.find("://",*end);
-
- if (m1 >= 0) //Easy match.
- {
- *begin = findHTMLToken(line, m1, TRUE);
- *end = findHTMLToken(line, m1, FALSE);
-
- //Load_url only handles http and https so don't hilite ftp, smb, etc.
- m2 = line.substr(*begin,(m1 - *begin)).find("http");
- m3 = line.substr(*begin,(m1 - *begin)).find("secondlife");
-
- std::string badneighbors=".,<>?';\"][}{=-+_)(*&^%$#@!~`\t\r\n\\";
-
- if (m2 >= 0 || m3>=0)
- {
- S32 bn = badneighbors.find(line.substr(m1+3,1));
-
- if (bn < 0)
- {
- matched = TRUE;
- }
- }
- }
-/* matches things like secondlife.com (no http://) needs a whitelist to really be effective.
- else //Harder match.
- {
- m1 = line.find(".",*end);
-
- if (m1 >= 0)
- {
- *end = findHTMLToken(line, m1, FALSE);
- *begin = findHTMLToken(line, m1, TRUE);
-
- m1 = line.rfind(".",*end);
-
- if ( ( *end - m1 ) > 2 && m1 > *begin)
- {
- std::string badneighbors=".,<>/?';\"][}{=-+_)(*&^%$#@!~`";
- m2 = badneighbors.find(line.substr(m1+1,1));
- m3 = badneighbors.find(line.substr(m1-1,1));
- if (m3<0 && m2<0)
- {
- matched = TRUE;
- }
- }
- }
- }
- */
-
- if (matched)
- {
- S32 strpos, strpos2;
-
- std::string url = line.substr(*begin,*end - *begin);
- std::string slurlID = "slurl.com/secondlife/";
- strpos = url.find(slurlID);
-
- if (strpos < 0)
- {
- slurlID="secondlife://";
- strpos = url.find(slurlID);
- }
-
- if (strpos < 0)
- {
- slurlID="sl://";
- strpos = url.find(slurlID);
- }
-
- if (strpos >= 0)
- {
- strpos+=slurlID.length();
-
- while ( ( strpos2=url.find("/",strpos) ) == -1 )
- {
- if ((*end+2) >= (S32)line.length() || line.substr(*end,1) != " " )
- {
- matched=FALSE;
- break;
- }
-
- strpos = (*end + 1) - *begin;
-
- *end = findHTMLToken(line,(*begin + strpos),FALSE);
- url = line.substr(*begin,*end - *begin);
- }
- }
-
- }
-
- if (!matched)
- {
- *begin=*end=0;
- }
- return matched;
-}
-
-
-
void LLTextEditor::updateAllowingLanguageInput()
{
LLWindow* window = getWindow();
@@ -4450,7 +2576,7 @@ void LLTextEditor::resetPreedit()
deselect();
}
- mCursorPos = mPreeditPositions.front();
+ setCursorPos(mPreeditPositions.front());
removeStringNoUndo(mCursorPos, mPreeditPositions.back() - mCursorPos);
insertStringNoUndo(mCursorPos, mPreeditOverwrittenWString);
@@ -4490,7 +2616,7 @@ void LLTextEditor::updatePreedit(const LLWString &preedit_string,
if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode())
{
- mPreeditOverwrittenWString = getWSubString(insert_preedit_at, mPreeditWString.length());
+ mPreeditOverwrittenWString = getWText().substr(insert_preedit_at, mPreeditWString.length());
removeStringNoUndo(insert_preedit_at, mPreeditWString.length());
}
else
@@ -4501,11 +2627,12 @@ 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.
- mKeystrokeTimer.reset();
+ resetCursorBlink();
+
+ onKeyStroke();
}
BOOL LLTextEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect *bounds, LLRect *control) const
@@ -4513,7 +2640,7 @@ BOOL LLTextEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect
if (control)
{
LLRect control_rect_screen;
- localRectToScreen(mTextRect, &control_rect_screen);
+ localRectToScreen(mVisibleTextRect, &control_rect_screen);
LLUI::screenRectToGL(control_rect_screen, control);
}
@@ -4534,7 +2661,7 @@ BOOL LLTextEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect
return FALSE;
}
- const S32 first_visible_line = mScrollbar->getDocPos();
+ const S32 first_visible_line = getFirstVisibleLine();
if (query < getLineStart(first_visible_line))
{
return FALSE;
@@ -4560,12 +2687,12 @@ BOOL LLTextEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect
const LLWString textString(getWText());
const llwchar * const text = textString.c_str();
- const S32 line_height = llround(mGLFont->getLineHeight());
+ const S32 line_height = llround(mDefaultFont->getLineHeight());
if (coord)
{
- const S32 query_x = mTextRect.mLeft + mGLFont->getWidth(text, current_line_start, query - current_line_start, mAllowEmbeddedItems);
- const S32 query_y = mTextRect.mTop - (current_line - first_visible_line) * line_height - line_height / 2;
+ const S32 query_x = mVisibleTextRect.mLeft + mDefaultFont->getWidth(text, current_line_start, query - current_line_start);
+ const S32 query_y = mVisibleTextRect.mTop - (current_line - first_visible_line) * line_height - line_height / 2;
S32 query_screen_x, query_screen_y;
localPointToScreen(query_x, query_y, &query_screen_x, &query_screen_y);
LLUI::screenPointToGL(query_screen_x, query_screen_y, &coord->mX, &coord->mY);
@@ -4573,23 +2700,23 @@ BOOL LLTextEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect
if (bounds)
{
- S32 preedit_left = mTextRect.mLeft;
+ S32 preedit_left = mVisibleTextRect.mLeft;
if (preedit_left_position > current_line_start)
{
- preedit_left += mGLFont->getWidth(text, current_line_start, preedit_left_position - current_line_start, mAllowEmbeddedItems);
+ preedit_left += mDefaultFont->getWidth(text, current_line_start, preedit_left_position - current_line_start);
}
- S32 preedit_right = mTextRect.mLeft;
+ S32 preedit_right = mVisibleTextRect.mLeft;
if (preedit_right_position < current_line_end)
{
- preedit_right += mGLFont->getWidth(text, current_line_start, preedit_right_position - current_line_start, mAllowEmbeddedItems);
+ preedit_right += mDefaultFont->getWidth(text, current_line_start, preedit_right_position - current_line_start);
}
else
{
- preedit_right += mGLFont->getWidth(text, current_line_start, current_line_end - current_line_start, mAllowEmbeddedItems);
+ preedit_right += mDefaultFont->getWidth(text, current_line_start, current_line_end - current_line_start);
}
- const S32 preedit_top = mTextRect.mTop - (current_line - first_visible_line) * line_height;
+ const S32 preedit_top = mVisibleTextRect.mTop - (current_line - first_visible_line) * line_height;
const S32 preedit_bottom = preedit_top - line_height;
const LLRect preedit_rect_local(preedit_left, preedit_top, preedit_right, preedit_bottom);
@@ -4663,10 +2790,39 @@ void LLTextEditor::markAsPreedit(S32 position, S32 length)
S32 LLTextEditor::getPreeditFontSize() const
{
- return llround(mGLFont->getLineHeight() * LLUI::sGLScaleFactor.mV[VY]);
+ return llround(mDefaultFont->getLineHeight() * LLUI::sGLScaleFactor.mV[VY]);
}
-LLWString LLTextEditor::getWText() const
+BOOL LLTextEditor::isDirty() const
+{
+ if(mReadOnly)
+ {
+ return FALSE;
+ }
+
+ if( mPristineCmd )
+ {
+ return ( mPristineCmd == mLastCmd );
+ }
+ else
+ {
+ return ( NULL != mLastCmd );
+ }
+}
+
+void LLTextEditor::setKeystrokeCallback(const keystroke_signal_t::slot_type& callback)
+{
+ mKeystrokeSignal.connect(callback);
+}
+
+void LLTextEditor::onKeyStroke()
+{
+ mKeystrokeSignal(this);
+}
+
+//virtual
+void LLTextEditor::clear()
{
- return getViewModel()->getDisplay();
+ getViewModel()->setDisplay(LLWStringUtil::null);
+ clearSegments();
}
diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h
index efedb30f47..58ecefdccb 100644
--- a/indra/llui/lltexteditor.h
+++ b/indra/llui/lltexteditor.h
@@ -2,31 +2,25 @@
* @file lltexteditor.h
* @brief LLTextEditor base class
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -37,13 +31,14 @@
#include "llrect.h"
#include "llkeywords.h"
-#include "lluictrl.h"
#include "llframetimer.h"
#include "lldarray.h"
#include "llstyle.h"
#include "lleditmenuhandler.h"
#include "lldarray.h"
#include "llviewborder.h" // for params
+#include "lltextbase.h"
+#include "lltextvalidate.h"
#include "llpreeditor.h"
#include "llcontrol.h"
@@ -51,65 +46,30 @@
class LLFontGL;
class LLScrollbar;
class LLKeywordToken;
-class LLTextCmd;
+class TextCmd;
class LLUICtrlFactory;
+class LLScrollContainer;
-class LLTextEditor : public LLUICtrl, LLEditMenuHandler, protected LLPreeditor
+class LLTextEditor :
+ public LLTextBase,
+ protected LLPreeditor
{
public:
- struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
+ struct Params : public LLInitParam::Block<Params, LLTextBase::Params>
{
Optional<std::string> default_text;
- Optional<S32> max_text_length;
+ Optional<LLTextValidate::validate_func_t, LLTextValidate::ValidateTextNamedFuncs> prevalidate_callback;
- Optional<bool> read_only,
- allow_embedded_items,
- hide_scrollbar,
- word_wrap,
+ Optional<bool> embedded_items,
ignore_tab,
- hide_border,
- track_bottom,
- takes_non_scroll_clicks;
+ show_line_numbers,
+ commit_on_focus_lost,
+ show_context_menu;
//colors
- Optional<LLUIColor> cursor_color,
- default_color,
- text_color,
- text_readonly_color,
- bg_readonly_color,
- bg_writeable_color,
- bg_focus_color;
-
- Optional<LLViewBorder::Params> border;
-
- Deprecated type,
- length,
- is_unicode;
-
-
- Params()
- : max_text_length("max_length", 255),
- read_only("read_only", false),
- allow_embedded_items("embedded_items", false),
- hide_scrollbar("hide_scrollbar", false),
- hide_border("hide_border", false),
- word_wrap("word_wrap", false),
- ignore_tab("ignore_tab", true),
- track_bottom("track_bottom", false),
- takes_non_scroll_clicks("takes_non_scroll_clicks", true),
- cursor_color("cursor_color"),
- default_color("default_color"),
- text_color("text_color"),
- text_readonly_color("text_readonly_color"),
- bg_readonly_color("bg_readonly_color"),
- bg_writeable_color("bg_writeable_color"),
- bg_focus_color("bg_focus_color"),
- length("length"),
- type("type"),
- is_unicode("is_unicode")
- {}
-
-
+ Optional<LLUIColor> default_color;
+
+ Params();
};
void initFromParams(const Params&);
@@ -126,39 +86,36 @@ public:
virtual ~LLTextEditor();
- void setParseHTML(BOOL parsing) {mParseHTML=parsing;}
+ typedef boost::signals2::signal<void (LLTextEditor* caller)> keystroke_signal_t;
+
+ void setKeystrokeCallback(const keystroke_signal_t::slot_type& callback);
+
void setParseHighlights(BOOL parsing) {mParseHighlights=parsing;}
// mousehandler overrides
virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask);
virtual BOOL handleHover(S32 x, S32 y, MASK mask);
- virtual BOOL handleScrollWheel(S32 x, S32 y, S32 clicks);
virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask );
virtual BOOL handleMiddleMouseDown(S32 x,S32 y,MASK mask);
virtual BOOL handleKeyHere(KEY key, MASK mask );
virtual BOOL handleUnicodeCharHere(llwchar uni_char);
- virtual BOOL handleToolTip(S32 x, S32 y, std::string& msg, LLRect* sticky_rect);
- virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
- EDragAndDropType cargo_type, void *cargo_data,
- EAcceptance *accept, std::string& tooltip_msg);
virtual void onMouseCaptureLost();
// view overrides
- virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
virtual void draw();
virtual void onFocusReceived();
virtual void onFocusLost();
+ virtual void onCommit();
virtual void setEnabled(BOOL enabled);
// uictrl overrides
- virtual void onTabInto();
virtual void clear();
virtual void setFocus( BOOL b );
- virtual BOOL acceptsTextInput() const;
- virtual BOOL isDirty() const { return( mLastCmd != NULL || (mPristineCmd && (mPristineCmd != mLastCmd)) ); }
+ virtual BOOL isDirty() const;
// LLEditMenuHandler interface
virtual void undo();
@@ -182,8 +139,6 @@ 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);
@@ -197,38 +152,27 @@ public:
BOOL isPristine() const;
BOOL allowsEmbeddedItems() const { return mAllowEmbeddedItems; }
+ //
+ // Text manipulation
+ //
+
// inserts text at cursor
void insertText(const std::string &text);
- // appends text at end
- void appendText(const std::string &wtext, bool allow_undo, bool prepend_newline,
- const LLStyleSP stylep = NULL);
-
- void appendColoredText(const std::string &wtext, bool allow_undo,
- bool prepend_newline,
- const LLColor4 &color,
- const std::string& font_name = LLStringUtil::null);
- // if styled text starts a line, you need to prepend a newline.
- void appendStyledText(const std::string &new_text, bool allow_undo,
- bool prepend_newline,
- LLStyleSP stylep = NULL);
- void appendHighlightedText(const std::string &new_text, bool allow_undo,
- bool prepend_newline, S32 highlight_part,
- LLStyleSP stylep);
-
+
+ void appendWidget(const LLInlineViewSegment::Params& params, const std::string& text, bool allow_undo);
+ // Non-undoable
+ void setText(const LLStringExplicit &utf8str, const LLStyle::Params& input_params = LLStyle::Params());
+
+
// Removes text from the end of document
// Does not change highlight or cursor position.
void removeTextFromEnd(S32 num_chars);
BOOL tryToRevertToPristineState();
- void setCursor(S32 row, S32 column);
- void setCursorPos(S32 offset);
void setCursorAndScrollToEnd();
- void getLineAndColumnForPosition( S32 position, S32* line, S32* col, BOOL include_wordwrap );
void getCurrentLineAndColumn( S32* line, S32* col, BOOL include_wordwrap );
- S32 getLineForPosition(S32 position);
- S32 getCurrentLine();
void loadKeywords(const std::string& filename,
const std::vector<std::string>& funcs,
@@ -237,197 +181,66 @@ public:
LLKeywords::keyword_iterator_t keywordsBegin() { return mKeywords.begin(); }
LLKeywords::keyword_iterator_t keywordsEnd() { return mKeywords.end(); }
- // Color support
- void setCursorColor(const LLColor4& c) { mCursorColor = c; }
- void setFgColor( const LLColor4& c ) { mFgColor = c; }
- void setTextDefaultColor( const LLColor4& c ) { mDefaultColor = c; }
- void setReadOnlyFgColor( const LLColor4& c ) { mReadOnlyFgColor = c; }
- void setWriteableBgColor( const LLColor4& c ) { mWriteableBgColor = c; }
- void setReadOnlyBgColor( const LLColor4& c ) { mReadOnlyBgColor = c; }
- void setTrackColor( const LLColor4& color );
- void setThumbColor( const LLColor4& color );
-
// Hacky methods to make it into a word-wrapping, potentially scrolling,
// read-only text box.
- void setBorderVisible(BOOL b);
- BOOL isBorderVisible() const;
- void setTakesNonScrollClicks(BOOL b) { mTakesNonScrollClicks = b; }
- void setHideScrollbarForShortDocs(BOOL b);
-
- void setWordWrap( BOOL b );
- void setTabsToNextField(BOOL b) { mTabsToNextField = b; }
- BOOL tabsToNextField() const { return mTabsToNextField; }
void setCommitOnFocusLost(BOOL b) { mCommitOnFocusLost = b; }
// Hack to handle Notecards
virtual BOOL importBuffer(const char* buffer, S32 length );
virtual BOOL exportBuffer(std::string& buffer );
- // If takes focus, will take keyboard focus on click.
- void setTakesFocus(BOOL b) { mTakesFocus = b; }
-
- void setSourceID(const LLUUID& id) { mSourceID = id; }
const LLUUID& getSourceID() const { return mSourceID; }
- void setAcceptCallingCardNames(BOOL enable) { mAcceptCallingCardNames = enable; }
- BOOL acceptsCallingCardNames() const { return mAcceptCallingCardNames; }
-
- void setHandleEditKeysDirectly( BOOL b ) { mHandleEditKeysDirectly = b; }
- // Callbacks
- static void setLinkColor(LLColor4 color) { mLinkColor = color; }
- static void setURLCallbacks(void (*callback1) (const std::string& url),
- bool (*callback2) (const std::string& url),
- bool (*callback3) (const std::string& url) )
- { mURLcallback = callback1; mSecondlifeURLcallback = callback2; mSecondlifeURLcallbackRightClick = callback3;}
+ const LLTextSegmentPtr getPreviousSegment() const;
+ void getSelectedSegments(segment_vec_t& segments) const;
- void setOnScrollEndCallback(void (*callback)(void*), void* userdata);
-
- // new methods
- void setValue(const LLSD& value);
-
- std::string getText() const;
-
- // Non-undoable
- void setText(const LLStringExplicit &utf8str);
- void setWText(const LLWString &wtext);
-
- // Returns byte length limit
- S32 getMaxLength() const { return mMaxTextByteLength; }
-
- // Change cursor
- void startOfLine();
- void endOfLine();
- void endOfDoc();
-
- BOOL isScrolledToTop();
- BOOL isScrolledToBottom();
-
- // Getters
- LLWString getWText() const;
- llwchar getWChar(S32 pos) const { return getWText()[pos]; }
- LLWString getWSubString(S32 pos, S32 len) const { return getWText().substr(pos, len); }
-
- const LLTextSegment* getCurrentSegment() const { return getSegmentAtOffset(mCursorPos); }
- const LLTextSegment* getPreviousSegment() const;
- void getSelectedSegments(std::vector<const LLTextSegment*>& segments) const;
-
- static bool isPartOfWord(llwchar c) { return (c == '_') || LLStringOps::isAlnum((char)c); }
+ void setShowContextMenu(bool show) { mShowContextMenu = show; }
+ bool getShowContextMenu() const { return mShowContextMenu; }
protected:
- //
- // Methods
- //
-
- S32 getLength() const { return getWText().length(); }
- void getSegmentAndOffset( S32 startpos, S32* segidxp, S32* offsetp ) const;
+ void showContextMenu(S32 x, S32 y);
void drawPreeditMarker();
- void updateLineStartList(S32 startpos = 0);
- void updateScrollFromCursor();
- void updateTextRect();
- const LLRect& getTextRect() const { return mTextRect; }
-
void assignEmbedded(const std::string &s);
- BOOL truncate(); // Returns true if truncation occurs
void removeCharOrTab();
- void setCursorAtLocalPos(S32 x, S32 y, BOOL round);
- S32 getCursorPosFromLocalCoord( S32 local_x, S32 local_y, BOOL round ) const;
void indentSelectedLines( S32 spaces );
S32 indentLine( S32 pos, S32 spaces );
void unindentLineBeforeCloseBrace();
- S32 getSegmentIdxAtOffset(S32 offset) const;
- const LLTextSegment* getSegmentAtLocalPos(S32 x, S32 y) const;
- const LLTextSegment* getSegmentAtOffset(S32 offset) const;
-
- 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 hasSelection() const { return (mSelectionStart !=mSelectionEnd); }
BOOL selectionContainsLineBreaks();
- void startSelection();
- void endSelection();
void deleteSelection(BOOL transient_operation);
S32 prevWordPos(S32 cursorPos) const;
S32 nextWordPos(S32 cursorPos) const;
- S32 getLineCount() const { return mLineStartList.size(); }
- S32 getLineStart( S32 line ) const;
- void getLineAndOffset(S32 pos, S32* linep, S32* offsetp) const;
- S32 getPos(S32 line, S32 offset);
-
- void changePage(S32 delta);
- void changeLine(S32 delta);
-
void autoIndent();
- void findEmbeddedItemSegments();
-
- virtual BOOL handleMouseUpOverSegment(S32 x, S32 y, MASK mask);
+ void findEmbeddedItemSegments(S32 start, S32 end);
+ void getSegmentsInRange(segment_vec_t& segments, S32 start, S32 end, bool include_partial) const;
virtual llwchar pasteEmbeddedItem(llwchar ext_char) { return ext_char; }
- virtual void bindEmbeddedChars(const LLFontGL* font) const {}
- virtual void unbindEmbeddedChars(const LLFontGL* font) const {}
- S32 findHTMLToken(const std::string &line, S32 pos, BOOL reverse) const;
- BOOL findHTML(const std::string &line, S32 *begin, S32 *end) const;
- // Abstract inner base class representing an undoable editor command.
- // Concrete sub-classes can be defined for operations such as insert, remove, etc.
- // Used as arguments to the execute() method below.
- class LLTextCmd
- {
- public:
- LLTextCmd( S32 pos, BOOL group_with_next ) : mPos(pos), mGroupWithNext(group_with_next) {}
- virtual ~LLTextCmd() {}
- virtual BOOL execute(LLTextEditor* editor, S32* delta) = 0;
- virtual S32 undo(LLTextEditor* editor) = 0;
- virtual S32 redo(LLTextEditor* editor) = 0;
- virtual BOOL canExtend(S32 pos) const { return FALSE; }
- virtual void blockExtensions() {}
- virtual BOOL extendAndExecute( LLTextEditor* editor, S32 pos, llwchar c, S32* delta ) { llassert(0); return 0; }
- virtual BOOL hasExtCharValue( llwchar value ) const { return FALSE; }
-
- // Defined here so they can access protected LLTextEditor editing methods
- S32 insert(LLTextEditor* editor, S32 pos, const LLWString &wstr) { return editor->insertStringNoUndo( pos, wstr ); }
- S32 remove(LLTextEditor* editor, S32 pos, S32 length) { return editor->removeStringNoUndo( pos, length ); }
- S32 overwrite(LLTextEditor* editor, S32 pos, llwchar wc) { return editor->overwriteCharNoUndo(pos, wc); }
-
- S32 getPosition() const { return mPos; }
- BOOL groupWithNext() const { return mGroupWithNext; }
-
- private:
- const S32 mPos;
- BOOL mGroupWithNext;
- };
// Here's the method that takes and applies text commands.
- S32 execute(LLTextCmd* cmd);
+ S32 execute(TextCmd* cmd);
// Undoable operations
void addChar(llwchar c); // at mCursorPos
S32 addChar(S32 pos, llwchar wc);
+ void addLineBreakChar();
S32 overwriteChar(S32 pos, llwchar wc);
void removeChar();
S32 removeChar(S32 pos);
- S32 insert(const S32 pos, const LLWString &wstr, const BOOL group_with_next_op);
- S32 remove(const S32 pos, const S32 length, const BOOL group_with_next_op);
- S32 append(const LLWString &wstr, const BOOL group_with_next_op);
+ S32 insert(S32 pos, const LLWString &wstr, bool group_with_next_op, LLTextSegmentPtr segment);
+ S32 remove(S32 pos, S32 length, bool group_with_next_op);
- // Direct operations
- S32 insertStringNoUndo(S32 pos, const LLWString &wstr); // returns num of chars actually inserted
- S32 removeStringNoUndo(S32 pos, S32 length);
- S32 overwriteCharNoUndo(S32 pos, llwchar wc);
-
- void resetKeystrokeTimer() { mKeystrokeTimer.reset(); }
-
void updateAllowingLanguageInput();
BOOL hasPreeditString() const;
@@ -440,6 +253,7 @@ protected:
virtual void getSelectionRange(S32 *position, S32 *length) const;
virtual BOOL getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect *bounds, LLRect *control) const;
virtual S32 getPreeditFontSize() const;
+ virtual LLWString getPreeditString() const { return getWText(); }
//
// Protected data
//
@@ -447,198 +261,75 @@ protected:
// as possible behind protected accessor methods.
//
- // I-beam is just after the mCursorPos-th character.
- S32 mCursorPos;
-
// Use these to determine if a click on an embedded item is a drag or not.
S32 mMouseDownX;
S32 mMouseDownY;
- // Are we in the middle of a drag-select? To figure out if there is a current
- // selection, call hasSelection().
- BOOL mIsSelecting;
- S32 mSelectionStart;
- S32 mSelectionEnd;
- S32 mLastSelectionX;
- S32 mLastSelectionY;
-
- BOOL mParseHTML;
- BOOL mParseHighlights;
- std::string mHTML;
-
- typedef std::vector<LLTextSegment *> segment_list_t;
- segment_list_t mSegments;
- const LLTextSegment* mHoverSegment;
-
- // Scrollbar data
- class LLScrollbar* mScrollbar;
- BOOL mHideScrollbarForShortDocs;
- BOOL mTakesNonScrollClicks;
- void (*mOnScrollEndCallback)(void*);
- void *mOnScrollEndData;
-
LLWString mPreeditWString;
LLWString mPreeditOverwrittenWString;
std::vector<S32> mPreeditPositions;
std::vector<BOOL> mPreeditStandouts;
-
-private:
+protected:
+ LLUIColor mDefaultColor;
+
+ BOOL mShowLineNumbers;
+
+ /*virtual*/ void updateSegments();
+ void updateLinkSegments();
+
+private:
//
// Methods
//
void pasteHelper(bool is_primary);
- virtual LLTextViewModel* getViewModel() const;
-
- void updateSegments();
- void pruneSegments();
-
- void drawBackground();
- void drawSelectionBackground();
- void drawCursor();
- void drawText();
- void drawClippedSegment(const LLWString &wtext, S32 seg_start, S32 seg_end, F32 x, F32 y, S32 selection_left, S32 selection_right, const LLStyleSP& color, F32* right_x);
-
- void needsReflow()
- {
- mReflowNeeded = TRUE;
- // cursor might have moved, need to scroll
- mScrollNeeded = TRUE;
- }
- void needsScroll() { mScrollNeeded = TRUE; }
+ void drawLineNumbers();
+
+ void onKeyStroke();
//
// Data
//
LLKeywords mKeywords;
- static LLColor4 mLinkColor;
- static void (*mURLcallback) (const std::string& url);
- static bool (*mSecondlifeURLcallback) (const std::string& url);
- static bool (*mSecondlifeURLcallbackRightClick) (const std::string& url);
-
- // Concrete LLTextCmd sub-classes used by the LLTextEditor base class
- class LLTextCmdInsert;
- class LLTextCmdAddChar;
- class LLTextCmdOverwriteChar;
- class LLTextCmdRemove;
- S32 mMaxTextByteLength; // Maximum length mText is allowed to be in bytes
-
- const LLFontGL* mGLFont;
- U8 mGLFontStyle; // the font style from xml
+ // Concrete TextCmd sub-classes used by the LLTextEditor base class
+ class TextCmdInsert;
+ class TextCmdAddChar;
+ class TextCmdOverwriteChar;
+ class TextCmdRemove;
class LLViewBorder* mBorder;
BOOL mBaseDocIsPristine;
- LLTextCmd* mPristineCmd;
+ TextCmd* mPristineCmd;
- LLTextCmd* mLastCmd;
+ TextCmd* mLastCmd;
- typedef std::deque<LLTextCmd*> undo_stack_t;
+ typedef std::deque<TextCmd*> undo_stack_t;
undo_stack_t mUndoStack;
- S32 mDesiredXPixel; // X pixel position where the user wants the cursor to be
- LLRect mTextRect; // The rect in which text is drawn. Excludes borders.
- // List of offsets and segment index of the start of each line. Always has at least one node (0).
- struct line_info
- {
- line_info(S32 segment, S32 offset) : mSegment(segment), mOffset(offset) {}
- S32 mSegment;
- S32 mOffset;
- };
- struct line_info_compare
- {
- bool operator()(const line_info& a, const line_info& b) const
- {
- if (a.mSegment < b.mSegment)
- return true;
- else if (a.mSegment > b.mSegment)
- return false;
- else
- return a.mOffset < b.mOffset;
- }
- };
- typedef std::vector<line_info> line_list_t;
- line_list_t mLineStartList;
- BOOL mReflowNeeded;
- BOOL mScrollNeeded;
-
- LLFrameTimer mKeystrokeTimer;
-
- LLUIColor mCursorColor;
- LLUIColor mFgColor;
- LLUIColor mDefaultColor;
- LLUIColor mReadOnlyFgColor;
- LLUIColor mWriteableBgColor;
- LLUIColor mReadOnlyBgColor;
- LLUIColor mFocusBgColor;
-
- BOOL mReadOnly;
- BOOL mWordWrap;
- BOOL mShowLineNumbers;
-
BOOL mTabsToNextField; // if true, tab moves focus to next field, else inserts spaces
BOOL mCommitOnFocusLost;
BOOL mTakesFocus;
- BOOL mTrackBottom; // if true, keeps scroll position at bottom during resize
- BOOL mScrolledToBottom;
BOOL mAllowEmbeddedItems;
-
- BOOL mAcceptCallingCardNames;
+ 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
-}; // end class LLTextEditor
-
+ keystroke_signal_t mKeystrokeSignal;
+ LLTextValidate::validate_func_t mPrevalidateFunc;
-class LLTextSegment
-{
-public:
- // for creating a compare value
- LLTextSegment(S32 start);
- LLTextSegment( const LLStyleSP& style, S32 start, S32 end );
- LLTextSegment( const LLColor4& color, S32 start, S32 end, BOOL is_visible);
- LLTextSegment( const LLColor4& color, S32 start, S32 end );
- LLTextSegment( const LLColor3& color, S32 start, S32 end );
-
- S32 getStart() const { return mStart; }
- S32 getEnd() const { return mEnd; }
- void setEnd( S32 end ) { mEnd = end; }
- const LLColor4& getColor() const { return mStyle->getColor(); }
- void setColor(const LLColor4 &color) { mStyle->setColor(color); }
- const LLStyleSP& getStyle() const { return mStyle; }
- void setStyle(const LLStyleSP &style) { mStyle = style; }
- void setIsDefault(BOOL b) { mIsDefault = b; }
- BOOL getIsDefault() const { return mIsDefault; }
- void setToken( LLKeywordToken* token ) { mToken = token; }
- LLKeywordToken* getToken() const { return mToken; }
- BOOL getToolTip( std::string& msg ) const;
-
- void dump() const;
-
- struct compare
- {
- bool operator()(const LLTextSegment* a, const LLTextSegment* b) const
- {
- return a->mStart < b->mStart;
- }
- };
-
-private:
- LLStyleSP mStyle;
- S32 mStart;
- S32 mEnd;
- LLKeywordToken* mToken;
- BOOL mIsDefault;
-};
+ LLContextMenu* mContextMenu;
+}; // end class LLTextEditor
+// Build time optimization, generate once in .cpp file
+#ifndef LLTEXTEDITOR_CPP
+extern template class LLTextEditor* LLView::getChild<class LLTextEditor>(
+ const std::string& name, BOOL recurse) const;
+#endif
#endif // LL_TEXTEDITOR_
diff --git a/indra/llui/lltextparser.cpp b/indra/llui/lltextparser.cpp
index 707dd0afdd..a4fe4f6ca8 100644
--- a/indra/llui/lltextparser.cpp
+++ b/indra/llui/lltextparser.cpp
@@ -1,31 +1,25 @@
/**
* @file lltextparser.cpp
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -43,29 +37,14 @@
#include "v4color.h"
#include "lldir.h"
-// Routines used for parsing text for TextParsers and html
-
-LLTextParser* LLTextParser::sInstance = NULL;
-
//
// Member Functions
//
-LLTextParser::~LLTextParser()
-{
- sInstance=NULL;
-}
+LLTextParser::LLTextParser()
+: mLoaded(false)
+{}
-// static
-LLTextParser* LLTextParser::getInstance()
-{
- if (!sInstance)
- {
- sInstance = new LLTextParser();
- sInstance->loadFromDisk();
- }
- return sInstance;
-}
// Moved triggerAlerts() to llfloaterchat.cpp to break llui/llaudio library dependency.
@@ -103,8 +82,10 @@ S32 LLTextParser::findPattern(const std::string &text, LLSD highlight)
return found;
}
-LLSD LLTextParser::parsePartialLineHighlights(const std::string &text, const LLColor4 &color, S32 part, S32 index)
+LLSD LLTextParser::parsePartialLineHighlights(const std::string &text, const LLColor4 &color, EHighlightPosition part, S32 index)
{
+ loadKeywords();
+
//evil recursive string atomizer.
LLSD ret_llsd, start_llsd, middle_llsd, end_llsd;
@@ -122,7 +103,7 @@ LLSD LLTextParser::parsePartialLineHighlights(const std::string &text, const LLC
{
S32 end = std::string(mHighlights[i]["pattern"]).length();
S32 len = text.length();
- S32 newpart;
+ EHighlightPosition newpart;
if (start==0)
{
start_llsd[0]["text"] =text.substr(0,end);
@@ -195,6 +176,8 @@ LLSD LLTextParser::parsePartialLineHighlights(const std::string &text, const LLC
bool LLTextParser::parseFullLineHighlights(const std::string &text, LLColor4 *color)
{
+ loadKeywords();
+
for (S32 i=0;i<mHighlights.size();i++)
{
if ((S32)mHighlights[i]["highlight"]==ALL || (S32)mHighlights[i]["condition"]==MATCHES)
@@ -221,14 +204,14 @@ std::string LLTextParser::getFileName()
return path;
}
-LLSD LLTextParser::loadFromDisk()
+void LLTextParser::loadKeywords()
{
- std::string filename=getFileName();
- if (filename.empty())
- {
- llwarns << "LLTextParser::loadFromDisk() no valid user directory." << llendl;
+ if (mLoaded)
+ {// keywords already loaded
+ return;
}
- else
+ std::string filename=getFileName();
+ if (!filename.empty())
{
llifstream file;
file.open(filename.c_str());
@@ -237,9 +220,8 @@ LLSD LLTextParser::loadFromDisk()
LLSDSerialize::fromXML(mHighlights, file);
}
file.close();
+ mLoaded = true;
}
-
- return mHighlights;
}
bool LLTextParser::saveToDisk(LLSD highlights)
diff --git a/indra/llui/lltextparser.h b/indra/llui/lltextparser.h
index fb1a7758b7..400aeeb8be 100644
--- a/indra/llui/lltextparser.h
+++ b/indra/llui/lltextparser.h
@@ -2,31 +2,25 @@
* @file llTextParser.h
* @brief GUI for user-defined highlights
*
- * $LicenseInfo:firstyear=2002&license=viewergpl$
- *
- * Copyright (c) 2002-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*
*/
@@ -34,38 +28,35 @@
#ifndef LL_LLTEXTPARSER_H
#define LL_LLTEXTPARSER_H
-#include "lltextparser.h"
-
#include "llsd.h"
+#include "llsingleton.h"
class LLUUID;
class LLVector3d;
class LLColor4;
-class LLTextParser
+class LLTextParser : public LLSingleton<LLTextParser>
{
public:
- enum ConditionType { CONTAINS, MATCHES, STARTS_WITH, ENDS_WITH };
- enum HighlightType { PART, ALL };
- enum HighlightPosition { WHOLE, START, MIDDLE, END };
- enum DialogAction { ACTION_NONE, ACTION_CLOSE, ACTION_ADD, ACTION_COPY, ACTION_UPDATE };
+ typedef enum e_condition_type { CONTAINS, MATCHES, STARTS_WITH, ENDS_WITH } EConditionType;
+ typedef enum e_highlight_type { PART, ALL } EHighlightType;
+ typedef enum e_highlight_position { WHOLE, START, MIDDLE, END } EHighlightPosition;
+ typedef enum e_dialog_action { ACTION_NONE, ACTION_CLOSE, ACTION_ADD, ACTION_COPY, ACTION_UPDATE } EDialogAction;
- static LLTextParser* getInstance();
- LLTextParser(){};
- ~LLTextParser();
+ LLTextParser();
- S32 findPattern(const std::string &text, LLSD highlight);
- LLSD parsePartialLineHighlights(const std::string &text,const LLColor4 &color,S32 part=WHOLE, S32 index=0);
+ LLSD parsePartialLineHighlights(const std::string &text,const LLColor4 &color, EHighlightPosition part=WHOLE, S32 index=0);
bool parseFullLineHighlights(const std::string &text, LLColor4 *color);
+private:
+ S32 findPattern(const std::string &text, LLSD highlight);
std::string getFileName();
- LLSD loadFromDisk();
+ void loadKeywords();
bool saveToDisk(LLSD highlights);
public:
LLSD mHighlights;
-private:
- static LLTextParser* sInstance;
+ bool mLoaded;
};
#endif
diff --git a/indra/llui/lltextutil.cpp b/indra/llui/lltextutil.cpp
new file mode 100644
index 0000000000..4df2c3363f
--- /dev/null
+++ b/indra/llui/lltextutil.cpp
@@ -0,0 +1,107 @@
+/**
+ * @file lltextutil.cpp
+ * @brief Misc text-related auxiliary methods
+ *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "lltextutil.h"
+
+#include "lluicolor.h"
+#include "lltextbox.h"
+#include "llurlmatch.h"
+
+boost::function<bool(LLUrlMatch*,LLTextBase*)> LLTextUtil::TextHelpers::iconCallbackCreationFunction = 0;
+
+void LLTextUtil::textboxSetHighlightedVal(LLTextBox *txtbox, const LLStyle::Params& normal_style, const std::string& text, const std::string& hl)
+{
+ static LLUIColor sFilterTextColor = LLUIColorTable::instance().getColor("FilterTextColor", LLColor4::green);
+
+ std::string text_uc = text;
+ LLStringUtil::toUpper(text_uc);
+
+ size_t hl_begin = 0, hl_len = hl.size();
+
+ if (hl_len == 0 || (hl_begin = text_uc.find(hl)) == std::string::npos)
+ {
+ txtbox->setText(text, normal_style);
+ return;
+ }
+
+ LLStyle::Params hl_style = normal_style;
+ hl_style.color = sFilterTextColor;
+
+ txtbox->setText(LLStringUtil::null); // clear text
+ txtbox->appendText(text.substr(0, hl_begin), false, normal_style);
+ txtbox->appendText(text.substr(hl_begin, hl_len), false, hl_style);
+ txtbox->appendText(text.substr(hl_begin + hl_len), false, normal_style);
+}
+
+const std::string& LLTextUtil::formatPhoneNumber(const std::string& phone_str)
+{
+ static const std::string PHONE_SEPARATOR = LLUI::sSettingGroups["config"]->getString("AvalinePhoneSeparator");
+ static const S32 PHONE_PART_LEN = 2;
+
+ static std::string formatted_phone_str;
+ formatted_phone_str = phone_str;
+ S32 separator_pos = (S32)(formatted_phone_str.size()) - PHONE_PART_LEN;
+ for (; separator_pos >= PHONE_PART_LEN; separator_pos -= PHONE_PART_LEN)
+ {
+ formatted_phone_str.insert(separator_pos, PHONE_SEPARATOR);
+ }
+
+ return formatted_phone_str;
+}
+
+bool LLTextUtil::processUrlMatch(LLUrlMatch* match,LLTextBase* text_base)
+{
+ if (match == 0 || text_base == 0)
+ return false;
+
+ if(match->getID() != LLUUID::null && TextHelpers::iconCallbackCreationFunction)
+ {
+ bool segment_created = TextHelpers::iconCallbackCreationFunction(match,text_base);
+ if(segment_created)
+ return true;
+ }
+
+ // output an optional icon before the Url
+ if (!match->getIcon().empty() )
+ {
+ LLUIImagePtr image = LLUI::getUIImage(match->getIcon());
+ if (image)
+ {
+ LLStyle::Params icon;
+ icon.image = image;
+ // Text will be replaced during rendering with the icon,
+ // but string cannot be empty or the segment won't be
+ // added (or drawn).
+ text_base->appendImageSegment(icon);
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// EOF
diff --git a/indra/llui/lltextutil.h b/indra/llui/lltextutil.h
new file mode 100644
index 0000000000..bf7dbb58ce
--- /dev/null
+++ b/indra/llui/lltextutil.h
@@ -0,0 +1,81 @@
+/**
+ * @file lltextutil.h
+ * @brief Misc text-related auxiliary methods
+ *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLTEXTUTIL_H
+#define LL_LLTEXTUTIL_H
+
+#include "llstyle.h"
+
+class LLTextBox;
+class LLUrlMatch;
+class LLTextBase;
+
+namespace LLTextUtil
+{
+
+ /**
+ * Set value for text box, highlighting substring hl_uc.
+ *
+ * Used to highlight filter matches.
+ *
+ * @param txtbox Text box to set value for
+ * @param normal_style Style to use for non-highlighted text
+ * @param text Text to set
+ * @param hl Upper-cased string to highlight
+ */
+ void textboxSetHighlightedVal(
+ LLTextBox *txtbox,
+ const LLStyle::Params& normal_style,
+ const std::string& text,
+ const std::string& hl);
+
+ /**
+ * Formats passed phone number to be more human readable.
+ *
+ * It just divides the number on parts by two digits from right to left. The first left part
+ * can have 2 or 3 digits, i.e. +44-33-33-44-55-66 or 12-34-56-78-90. Separator is set in
+ * application settings (AvalinePhoneSeparator)
+ *
+ * @param[in] phone_str string with original phone number
+ * @return reference to string with formatted phone number
+ */
+ const std::string& formatPhoneNumber(const std::string& phone_str);
+
+ bool processUrlMatch(LLUrlMatch* match,LLTextBase* text_base);
+
+ class TextHelpers
+ {
+
+ //we need this special callback since we need to create LLAvataIconCtrls while parsing
+ //avatar/group url but can't create LLAvataIconCtrl from LLUI
+ public:
+ static boost::function<bool(LLUrlMatch*,LLTextBase*)> iconCallbackCreationFunction;
+ };
+
+
+}
+
+#endif // LL_LLTEXTUTIL_H
diff --git a/indra/llui/lltextvalidate.cpp b/indra/llui/lltextvalidate.cpp
new file mode 100644
index 0000000000..4b9faa0560
--- /dev/null
+++ b/indra/llui/lltextvalidate.cpp
@@ -0,0 +1,314 @@
+/**
+ * @file lltextvalidate.cpp
+ * @brief Text validation helper functions
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+// 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);
+ declare("ascii_with_newline", validateASCIIWithNewLine);
+ }
+
+ // 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;
+ }
+
+ // Used for multiline text stored on the server.
+ // Example is landmark description in Places SP.
+ bool validateASCIIWithNewLine(const LLWString &str)
+ {
+ bool rv = TRUE;
+ S32 len = str.length();
+ while(len--)
+ {
+ if (str[len] < 0x20 && str[len] != 0xA || 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..84644be30c
--- /dev/null
+++ b/indra/llui/lltextvalidate.h
@@ -0,0 +1,58 @@
+/**
+ * @file lltextbase.h
+ * @author Martin Reddy
+ * @brief The base class of text box/editor, providing Url handling support
+ *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_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);
+ bool validateASCIIWithNewLine(const LLWString &str);
+}
+
+
+#endif
diff --git a/indra/llui/lltoggleablemenu.cpp b/indra/llui/lltoggleablemenu.cpp
new file mode 100644
index 0000000000..0eb2dc1387
--- /dev/null
+++ b/indra/llui/lltoggleablemenu.cpp
@@ -0,0 +1,83 @@
+/**
+ * @file lltoggleablemenu.cpp
+ * @brief Menu toggled by a button press
+ *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+
+#include "linden_common.h"
+
+#include "lltoggleablemenu.h"
+#include "lluictrlfactory.h"
+
+static LLDefaultChildRegistry::Register<LLToggleableMenu> r("toggleable_menu");
+
+LLToggleableMenu::LLToggleableMenu(const LLToggleableMenu::Params& p)
+: LLMenuGL(p),
+ mButtonRect(),
+ mClosedByButtonClick(false)
+{
+}
+
+// virtual
+void LLToggleableMenu::handleVisibilityChange (BOOL curVisibilityIn)
+{
+ S32 x,y;
+ LLUI::getMousePositionLocal(LLUI::getRootView(), &x, &y);
+
+ if (!curVisibilityIn && mButtonRect.pointInRect(x, y))
+ {
+ mClosedByButtonClick = true;
+ }
+}
+
+void LLToggleableMenu::setButtonRect(const LLRect& rect, LLView* current_view)
+{
+ LLRect screen;
+ current_view->localRectToScreen(rect, &screen);
+ mButtonRect = screen;
+}
+
+void LLToggleableMenu::setButtonRect(LLView* current_view)
+{
+ LLRect rect = current_view->getLocalRect();
+ setButtonRect(rect, current_view);
+}
+
+bool LLToggleableMenu::toggleVisibility()
+{
+ if (mClosedByButtonClick)
+ {
+ mClosedByButtonClick = false;
+ return false;
+ }
+
+ if (getVisible())
+ {
+ setVisible(FALSE);
+ mClosedByButtonClick = false;
+ return false;
+ }
+
+ return true;
+}
diff --git a/indra/llui/lltoggleablemenu.h b/indra/llui/lltoggleablemenu.h
new file mode 100644
index 0000000000..f036cdfffb
--- /dev/null
+++ b/indra/llui/lltoggleablemenu.h
@@ -0,0 +1,62 @@
+/**
+ * @file lltoggleablemenu.h
+ * @brief Menu toggled by a button press
+ *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLTOGGLEABLEMENU_H
+#define LL_LLTOGGLEABLEMENU_H
+
+#include "llmenugl.h"
+
+class LLToggleableMenu : public LLMenuGL
+{
+public:
+ //adding blank params to work around registration issue
+ //where LLToggleableMenu was owning the LLMenuGL param
+ //and menu.xml was never loaded
+ struct Params : public LLInitParam::Block<Params, LLMenuGL::Params>
+ {};
+protected:
+ LLToggleableMenu(const Params&);
+ friend class LLUICtrlFactory;
+public:
+ virtual void handleVisibilityChange (BOOL curVisibilityIn);
+
+ const LLRect& getButtonRect() const { return mButtonRect; }
+
+ // Converts the given local button rect to a screen rect
+ void setButtonRect(const LLRect& rect, LLView* current_view);
+ void setButtonRect(LLView* current_view);
+
+ // Returns "true" if menu was not closed by button click
+ // and is not still visible. If menu is visible toggles
+ // its visibility off.
+ bool toggleVisibility();
+
+protected:
+ bool mClosedByButtonClick;
+ LLRect mButtonRect;
+};
+
+#endif // LL_LLTOGGLEABLEMENU_H
diff --git a/indra/llui/lltooltip.cpp b/indra/llui/lltooltip.cpp
new file mode 100644
index 0000000000..b02d3122fe
--- /dev/null
+++ b/indra/llui/lltooltip.cpp
@@ -0,0 +1,589 @@
+/**
+ * @file lltooltip.cpp
+ * @brief LLToolTipMgr class implementation and related classes
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+// self include
+#include "lltooltip.h"
+
+// Library includes
+#include "lltextbox.h"
+#include "lliconctrl.h"
+#include "llbutton.h"
+#include "llmenugl.h" // hideMenus()
+#include "llui.h" // positionViewNearMouse()
+#include "llwindow.h"
+#include "lltrans.h"
+//
+// Constants
+//
+
+//
+// Local globals
+//
+
+LLToolTipView *gToolTipView = NULL;
+
+//
+// Member functions
+//
+
+static LLDefaultChildRegistry::Register<LLToolTipView> register_tooltip_view("tooltip_view");
+
+LLToolTipView::Params::Params()
+{
+ mouse_opaque = false;
+}
+
+LLToolTipView::LLToolTipView(const LLToolTipView::Params& p)
+: LLView(p)
+{
+}
+
+void LLToolTipView::draw()
+{
+ LLToolTipMgr::instance().updateToolTipVisibility();
+
+ // do the usual thing
+ LLView::draw();
+}
+
+BOOL LLToolTipView::handleHover(S32 x, S32 y, MASK mask)
+{
+ static S32 last_x = x;
+ static S32 last_y = y;
+
+ LLToolTipMgr& tooltip_mgr = LLToolTipMgr::instance();
+
+ if (x != last_x && y != last_y)
+ {
+ // allow new tooltips because mouse moved
+ tooltip_mgr.unblockToolTips();
+ }
+
+ last_x = x;
+ last_y = y;
+ return LLView::handleHover(x, y, mask);
+}
+
+BOOL LLToolTipView::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ LLToolTipMgr::instance().blockToolTips();
+
+ if (LLView::handleMouseDown(x, y, mask))
+ {
+ // If we are handling the mouse event menu holder
+ // won't get a chance to close menus so do this here
+ LLMenuGL::sMenuContainer->hideMenus();
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+BOOL LLToolTipView::handleMiddleMouseDown(S32 x, S32 y, MASK mask)
+{
+ LLToolTipMgr::instance().blockToolTips();
+ return LLView::handleMiddleMouseDown(x, y, mask);
+}
+
+BOOL LLToolTipView::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+ LLToolTipMgr::instance().blockToolTips();
+ return LLView::handleRightMouseDown(x, y, mask);
+}
+
+
+BOOL LLToolTipView::handleScrollWheel( S32 x, S32 y, S32 clicks )
+{
+ LLToolTipMgr::instance().blockToolTips();
+ return FALSE;
+}
+
+void LLToolTipView::drawStickyRect()
+{
+ gl_rect_2d(LLToolTipMgr::instance().getMouseNearRect(), LLColor4::white, false);
+}
+
+// defaults for floater param block pulled from widgets/floater.xml
+static LLWidgetNameRegistry::StaticRegistrar sRegisterInspectorParams(&typeid(LLInspector::Params), "inspector");
+
+//
+// LLToolTip
+//
+
+
+static LLDefaultChildRegistry::Register<LLToolTip> register_tooltip("tool_tip");
+
+
+LLToolTip::Params::Params()
+: max_width("max_width", 200),
+ padding("padding", 4),
+ wrap("wrap", true),
+ pos("pos"),
+ message("message"),
+ delay_time("delay_time", LLUI::sSettingGroups["config"]->getF32( "ToolTipDelay" )),
+ visible_time_over("visible_time_over", LLUI::sSettingGroups["config"]->getF32( "ToolTipVisibleTimeOver" )),
+ visible_time_near("visible_time_near", LLUI::sSettingGroups["config"]->getF32( "ToolTipVisibleTimeNear" )),
+ visible_time_far("visible_time_far", LLUI::sSettingGroups["config"]->getF32( "ToolTipVisibleTimeFar" )),
+ sticky_rect("sticky_rect"),
+ image("image"),
+ text_color("text_color"),
+ time_based_media("time_based_media", false),
+ web_based_media("web_based_media", false),
+ media_playing("media_playing", false)
+{
+ chrome = true;
+}
+
+LLToolTip::LLToolTip(const LLToolTip::Params& p)
+: LLPanel(p),
+ mHasClickCallback(p.click_callback.isProvided()),
+ mPadding(p.padding),
+ mTextBox(NULL),
+ mInfoButton(NULL),
+ mPlayMediaButton(NULL),
+ mHomePageButton(NULL)
+{
+ LLTextBox::Params params;
+ params.name = params.initial_value().asString();
+ // bake textbox padding into initial rect
+ params.rect = LLRect (mPadding, mPadding + 1, mPadding + 1, mPadding);
+ params.h_pad = 0;
+ params.v_pad = 0;
+ params.mouse_opaque = false;
+ params.text_color = p.text_color;
+ params.bg_visible = false;
+ params.font = p.font;
+ params.use_ellipses = true;
+ params.wrap = p.wrap;
+ params.parse_urls = false; // disallow hyperlinks in tooltips, as they want to spawn their own explanatory tooltips
+ mTextBox = LLUICtrlFactory::create<LLTextBox> (params);
+ addChild(mTextBox);
+
+ S32 TOOLTIP_ICON_SIZE = 0;
+ S32 TOOLTIP_PLAYBUTTON_SIZE = 0;
+ if (p.image.isProvided())
+ {
+ LLButton::Params icon_params;
+ icon_params.name = "tooltip_info";
+ icon_params.label(""); // provid label but set to empty so name does not overwrite it -angela
+ LLRect icon_rect;
+ LLUIImage* imagep = p.image;
+ TOOLTIP_ICON_SIZE = (imagep ? imagep->getWidth() : 16);
+ icon_rect.setOriginAndSize(mPadding, mPadding, TOOLTIP_ICON_SIZE, TOOLTIP_ICON_SIZE);
+ icon_params.rect = icon_rect;
+ icon_params.image_unselected(imagep);
+ icon_params.image_selected(imagep);
+
+ icon_params.scale_image(true);
+ icon_params.flash_color(icon_params.highlight_color());
+ mInfoButton = LLUICtrlFactory::create<LLButton>(icon_params);
+ if (p.click_callback.isProvided())
+ {
+ mInfoButton->setCommitCallback(boost::bind(p.click_callback()));
+ }
+ addChild(mInfoButton);
+
+ // move text over to fit image in
+ mTextBox->translate(TOOLTIP_ICON_SIZE + mPadding, 0);
+ }
+
+ if (p.time_based_media)
+ {
+ LLButton::Params p_button;
+ p_button.name(std::string("play_media"));
+ p_button.label(""); // provide label but set to empty so name does not overwrite it -angela
+ TOOLTIP_PLAYBUTTON_SIZE = 16;
+ LLRect button_rect;
+ button_rect.setOriginAndSize((mPadding +TOOLTIP_ICON_SIZE+ mPadding ), mPadding, TOOLTIP_ICON_SIZE, TOOLTIP_ICON_SIZE);
+ p_button.rect = button_rect;
+ p_button.image_selected.name("button_anim_pause.tga");
+ p_button.image_unselected.name("button_anim_play.tga");
+ p_button.scale_image(true);
+
+ mPlayMediaButton = LLUICtrlFactory::create<LLButton>(p_button);
+ if(p.click_playmedia_callback.isProvided())
+ {
+ mPlayMediaButton->setCommitCallback(boost::bind(p.click_playmedia_callback()));
+ }
+ mPlayMediaButton->setToggleState(p.media_playing);
+ addChild(mPlayMediaButton);
+
+ // move text over to fit image in
+ mTextBox->translate(TOOLTIP_PLAYBUTTON_SIZE + mPadding, 0);
+ }
+
+ if (p.web_based_media)
+ {
+ LLButton::Params p_w_button;
+ p_w_button.name(std::string("home_page"));
+ p_w_button.label(""); // provid label but set to empty so name does not overwrite it -angela
+ TOOLTIP_PLAYBUTTON_SIZE = 16;
+ LLRect button_rect;
+ button_rect.setOriginAndSize((mPadding +TOOLTIP_ICON_SIZE+ mPadding ), mPadding, TOOLTIP_ICON_SIZE, TOOLTIP_ICON_SIZE);
+ p_w_button.rect = button_rect;
+ p_w_button.image_unselected.name("map_home.tga");
+ p_w_button.scale_image(true);
+
+ mHomePageButton = LLUICtrlFactory::create<LLButton>(p_w_button);
+ if(p.click_homepage_callback.isProvided())
+ {
+ mHomePageButton->setCommitCallback(boost::bind(p.click_homepage_callback()));
+ }
+ addChild(mHomePageButton);
+
+ // move text over to fit image in
+ mTextBox->translate(TOOLTIP_PLAYBUTTON_SIZE + mPadding, 0);
+ }
+
+ if (p.click_callback.isProvided())
+ {
+ setMouseUpCallback(boost::bind(p.click_callback()));
+ }
+}
+
+void LLToolTip::initFromParams(const LLToolTip::Params& p)
+{
+ LLPanel::initFromParams(p);
+
+ // do this *after* we've had our size set in LLPanel::initFromParams();
+ const S32 REALLY_LARGE_HEIGHT = 10000;
+ mTextBox->reshape(p.max_width, REALLY_LARGE_HEIGHT);
+
+ if (p.styled_message.isProvided())
+ {
+ for (LLInitParam::ParamIterator<LLToolTip::StyledText>::const_iterator text_it = p.styled_message().begin();
+ text_it != p.styled_message().end();
+ ++text_it)
+ {
+ mTextBox->appendText(text_it->text(), false, text_it->style);
+ }
+ }
+ else
+ {
+ mTextBox->setText(p.message());
+ }
+
+ S32 text_width = llmin(p.max_width(), mTextBox->getTextPixelWidth());
+ S32 text_height = mTextBox->getTextPixelHeight();
+ mTextBox->reshape(text_width, text_height);
+
+ // reshape tooltip panel to fit text box
+ LLRect tooltip_rect = calcBoundingRect();
+ tooltip_rect.mTop += mPadding;
+ tooltip_rect.mRight += mPadding;
+ tooltip_rect.mBottom = 0;
+ tooltip_rect.mLeft = 0;
+
+ setShape(tooltip_rect);
+}
+
+void LLToolTip::setVisible(BOOL visible)
+{
+ // fade out tooltip over time
+ if (visible)
+ {
+ mVisibleTimer.start();
+ mFadeTimer.stop();
+ LLPanel::setVisible(TRUE);
+ }
+ else
+ {
+ mVisibleTimer.stop();
+ // don't actually change mVisible state, start fade out transition instead
+ if (!mFadeTimer.getStarted())
+ {
+ mFadeTimer.start();
+ }
+ }
+}
+
+BOOL LLToolTip::handleHover(S32 x, S32 y, MASK mask)
+{
+ //mInfoButton->setFlashing(true);
+ if(mInfoButton)
+ mInfoButton->setHighlight(true);
+
+ LLPanel::handleHover(x, y, mask);
+ if (mHasClickCallback)
+ {
+ getWindow()->setCursor(UI_CURSOR_HAND);
+ }
+ return TRUE;
+}
+
+void LLToolTip::onMouseLeave(S32 x, S32 y, MASK mask)
+{
+ //mInfoButton->setFlashing(true);
+ if(mInfoButton)
+ mInfoButton->setHighlight(false);
+ LLUICtrl::onMouseLeave(x, y, mask);
+}
+
+void LLToolTip::draw()
+{
+ F32 alpha = 1.f;
+
+ if (mFadeTimer.getStarted())
+ {
+ F32 tool_tip_fade_time = LLUI::sSettingGroups["config"]->getF32("ToolTipFadeTime");
+ alpha = clamp_rescale(mFadeTimer.getElapsedTimeF32(), 0.f, tool_tip_fade_time, 1.f, 0.f);
+ if (alpha == 0.f)
+ {
+ // finished fading out, so hide ourselves
+ mFadeTimer.stop();
+ LLPanel::setVisible(false);
+ }
+ }
+
+ // draw tooltip contents with appropriate alpha
+ {
+ LLViewDrawContext context(alpha);
+ LLPanel::draw();
+ }
+}
+
+bool LLToolTip::isFading()
+{
+ return mFadeTimer.getStarted();
+}
+
+F32 LLToolTip::getVisibleTime()
+{
+ return mVisibleTimer.getStarted() ? mVisibleTimer.getElapsedTimeF32() : 0.f;
+}
+
+bool LLToolTip::hasClickCallback()
+{
+ return mHasClickCallback;
+}
+
+
+//
+// LLToolTipMgr
+//
+
+LLToolTipMgr::LLToolTipMgr()
+: mToolTipsBlocked(false),
+ mToolTip(NULL),
+ mNeedsToolTip(false)
+{}
+
+void LLToolTipMgr::createToolTip(const LLToolTip::Params& params)
+{
+ // block all other tooltips until tooltips re-enabled (e.g. mouse moved)
+ blockToolTips();
+
+ delete mToolTip;
+
+ LLToolTip::Params tooltip_params(params);
+ // block mouse events if there is a click handler registered (specifically, hover)
+ if (params.click_callback.isProvided())
+ {
+ // set mouse_opaque to true if it wasn't already set to something else
+ // this prevents mouse down from going "through" the tooltip and ultimately
+ // causing the tooltip to disappear
+ tooltip_params.mouse_opaque.setIfNotProvided(true);
+ }
+ tooltip_params.rect = LLRect (0, 1, 1, 0);
+
+ mToolTip = LLUICtrlFactory::create<LLToolTip> (tooltip_params);
+
+ gToolTipView->addChild(mToolTip);
+
+ if (params.pos.isProvided())
+ {
+ LLCoordGL pos = params.pos;
+ // try to spawn at requested position
+ LLUI::positionViewNearMouse(mToolTip, pos.mX, pos.mY);
+ }
+ else
+ {
+ // just spawn at mouse location
+ LLUI::positionViewNearMouse(mToolTip);
+ }
+
+ //...update "sticky" rect and tooltip position
+ if (params.sticky_rect.isProvided())
+ {
+ mMouseNearRect = params.sticky_rect;
+ }
+ else
+ {
+ S32 mouse_x;
+ S32 mouse_y;
+ LLUI::getMousePositionLocal(gToolTipView->getParent(), &mouse_x, &mouse_y);
+
+ // allow mouse a little bit of slop before changing tooltips
+ mMouseNearRect.setCenterAndSize(mouse_x, mouse_y, 3, 3);
+ }
+
+ // allow mouse to move all the way to the tooltip without changing tooltips
+ // (tooltip can still time out)
+ if (mToolTip->hasClickCallback())
+ {
+ // keep tooltip up when we mouse over it
+ mMouseNearRect.unionWith(mToolTip->getRect());
+ }
+}
+
+
+void LLToolTipMgr::show(const std::string& msg)
+{
+ show(LLToolTip::Params().message(msg));
+}
+
+void LLToolTipMgr::show(const LLToolTip::Params& params)
+{
+ // fill in default tooltip params from tool_tip.xml
+ LLToolTip::Params params_with_defaults(params);
+ params_with_defaults.fillFrom(LLUICtrlFactory::instance().getDefaultParams<LLToolTip>());
+ if (!params_with_defaults.validateBlock())
+ {
+ llwarns << "Could not display tooltip!" << llendl;
+ return;
+ }
+
+ S32 mouse_x;
+ S32 mouse_y;
+ LLUI::getMousePositionLocal(gToolTipView, &mouse_x, &mouse_y);
+
+ // are we ready to show the tooltip?
+ if (!mToolTipsBlocked // we haven't hit a key, moved the mouse, etc.
+ && LLUI::getMouseIdleTime() > params_with_defaults.delay_time) // the mouse has been still long enough
+ {
+ bool tooltip_changed = mLastToolTipParams.message() != params_with_defaults.message()
+ || mLastToolTipParams.pos() != params_with_defaults.pos()
+ || mLastToolTipParams.time_based_media() != params_with_defaults.time_based_media()
+ || mLastToolTipParams.web_based_media() != params_with_defaults.web_based_media();
+
+ bool tooltip_shown = mToolTip
+ && mToolTip->getVisible()
+ && !mToolTip->isFading();
+
+ mNeedsToolTip = tooltip_changed || !tooltip_shown;
+ // store description of tooltip for later creation
+ mNextToolTipParams = params_with_defaults;
+ }
+}
+
+// allow new tooltips to be created, e.g. after mouse has moved
+void LLToolTipMgr::unblockToolTips()
+{
+ mToolTipsBlocked = false;
+}
+
+// disallow new tooltips until unblockTooltips called
+void LLToolTipMgr::blockToolTips()
+{
+ hideToolTips();
+ mToolTipsBlocked = true;
+}
+
+void LLToolTipMgr::hideToolTips()
+{
+ if (mToolTip)
+ {
+ mToolTip->setVisible(FALSE);
+ }
+}
+
+bool LLToolTipMgr::toolTipVisible()
+{
+ return mToolTip ? mToolTip->isInVisibleChain() : false;
+}
+
+LLRect LLToolTipMgr::getToolTipRect()
+{
+ if (mToolTip && mToolTip->getVisible())
+ {
+ return mToolTip->getRect();
+ }
+ return LLRect();
+}
+
+
+LLRect LLToolTipMgr::getMouseNearRect()
+{
+ return toolTipVisible() ? mMouseNearRect : LLRect();
+}
+
+// every frame, determine if current tooltip should be hidden
+void LLToolTipMgr::updateToolTipVisibility()
+{
+ // create new tooltip if we have one ready to go
+ if (mNeedsToolTip)
+ {
+ mNeedsToolTip = false;
+ createToolTip(mNextToolTipParams);
+ mLastToolTipParams = mNextToolTipParams;
+
+ return;
+ }
+
+ // hide tooltips when mouse cursor is hidden
+ if (LLUI::getWindow()->isCursorHidden())
+ {
+ blockToolTips();
+ return;
+ }
+
+ // hide existing tooltips if they have timed out
+ S32 mouse_x, mouse_y;
+ LLUI::getMousePositionLocal(gToolTipView, &mouse_x, &mouse_y);
+
+ F32 tooltip_timeout = 0.f;
+ if (toolTipVisible())
+ {
+ // mouse far away from tooltip
+ tooltip_timeout = mLastToolTipParams.visible_time_far;
+ // mouse near rect will only include the tooltip if the
+ // tooltip is clickable
+ if (mMouseNearRect.pointInRect(mouse_x, mouse_y))
+ {
+ // mouse "close" to tooltip
+ tooltip_timeout = mLastToolTipParams.visible_time_near;
+
+ // if tooltip is clickable (has large mMouseNearRect)
+ // than having cursor over tooltip keeps it up indefinitely
+ if (mToolTip->parentPointInView(mouse_x, mouse_y))
+ {
+ // mouse over tooltip itself, don't time out
+ tooltip_timeout = mLastToolTipParams.visible_time_over;
+ }
+ }
+
+ if (mToolTip->getVisibleTime() > tooltip_timeout)
+ {
+ hideToolTips();
+ }
+ }
+}
+
+
+
+// EOF
diff --git a/indra/llui/lltooltip.h b/indra/llui/lltooltip.h
new file mode 100644
index 0000000000..d71a944c3d
--- /dev/null
+++ b/indra/llui/lltooltip.h
@@ -0,0 +1,166 @@
+/**
+ * @file lltooltip.h
+ * @brief LLToolTipMgr class definition and related classes
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLTOOLTIP_H
+#define LL_LLTOOLTIP_H
+
+// Library includes
+#include "llsingleton.h"
+#include "llinitparam.h"
+#include "llpanel.h"
+#include "llstyle.h"
+
+//
+// Classes
+//
+class LLToolTipView : public LLView
+{
+public:
+ struct Params : public LLInitParam::Block<Params, LLView::Params>
+ {
+ Params();
+ };
+ LLToolTipView(const LLToolTipView::Params&);
+ /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleMiddleMouseDown(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleScrollWheel( S32 x, S32 y, S32 clicks );
+
+ void drawStickyRect();
+
+ /*virtual*/ void draw();
+};
+
+class LLToolTip : public LLPanel
+{
+public:
+
+ struct StyledText : public LLInitParam::Block<StyledText>
+ {
+ Mandatory<std::string> text;
+ Optional<LLStyle::Params> style;
+ };
+
+ struct Params : public LLInitParam::Block<Params, LLPanel::Params>
+ {
+ typedef boost::function<void(void)> click_callback_t;
+
+ Optional<std::string> message;
+ Multiple<StyledText> styled_message;
+
+ Optional<LLCoordGL> pos;
+ Optional<F32> delay_time,
+ visible_time_over, // time for which tooltip is visible while mouse on it
+ visible_time_near, // time for which tooltip is visible while mouse near it
+ visible_time_far; // time for which tooltip is visible while mouse moved away
+ Optional<LLRect> sticky_rect;
+ Optional<const LLFontGL*> font;
+ Optional<LLUIImage*> image;
+ Optional<LLUIColor> text_color;
+ Optional<bool> time_based_media,
+ web_based_media,
+ media_playing;
+ Optional<click_callback_t> click_callback,
+ click_playmedia_callback,
+ click_homepage_callback;
+ Optional<S32> max_width,
+ padding;
+ Optional<bool> wrap;
+
+ Params();
+ };
+ /*virtual*/ void draw();
+ /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask);
+ /*virtual*/ void onMouseLeave(S32 x, S32 y, MASK mask);
+ /*virtual*/ void setVisible(BOOL visible);
+
+ bool isFading();
+ F32 getVisibleTime();
+ bool hasClickCallback();
+
+ LLToolTip(const Params& p);
+ void initFromParams(const LLToolTip::Params& params);
+
+private:
+ class LLTextBox* mTextBox;
+ class LLButton* mInfoButton;
+ class LLButton* mPlayMediaButton;
+ class LLButton* mHomePageButton;
+
+ LLFrameTimer mFadeTimer;
+ LLFrameTimer mVisibleTimer;
+ bool mHasClickCallback;
+ S32 mPadding; // pixels
+};
+
+// used for the inspector tooltips which need different background images etc.
+class LLInspector : public LLToolTip
+{
+public:
+ struct Params : public LLInitParam::Block<Params, LLToolTip::Params>
+ {};
+};
+
+class LLToolTipMgr : public LLSingleton<LLToolTipMgr>
+{
+ LOG_CLASS(LLToolTipMgr);
+public:
+ LLToolTipMgr();
+ void show(const LLToolTip::Params& params);
+ void show(const std::string& message);
+
+ void unblockToolTips();
+ void blockToolTips();
+
+ void hideToolTips();
+ bool toolTipVisible();
+ LLRect getToolTipRect();
+ LLRect getMouseNearRect();
+ void updateToolTipVisibility();
+
+private:
+ void createToolTip(const LLToolTip::Params& params);
+
+ bool mToolTipsBlocked;
+ class LLToolTip* mToolTip;
+
+ // tooltip creation is deferred until the UI is drawn every frame
+ // so the last tooltip to be created in a given frame will win
+ LLToolTip::Params mLastToolTipParams; // description of last tooltip we showed
+ LLToolTip::Params mNextToolTipParams; // description of next tooltip we want to show
+ bool mNeedsToolTip; // do we want to show a tooltip
+
+ LLRect mMouseNearRect;
+};
+
+//
+// Globals
+//
+
+extern LLToolTipView *gToolTipView;
+
+#endif
diff --git a/indra/llui/lltransutil.cpp b/indra/llui/lltransutil.cpp
new file mode 100644
index 0000000000..9d0ff9d5cb
--- /dev/null
+++ b/indra/llui/lltransutil.cpp
@@ -0,0 +1,61 @@
+/**
+ * @file lltrans.cpp
+ * @brief LLTrans implementation
+ *
+ * $LicenseInfo:firstyear=2000&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "lltrans.h"
+#include "lluictrlfactory.h"
+
+#include "lltransutil.h"
+
+
+bool LLTransUtil::parseStrings(const std::string& xml_filename, const std::set<std::string>& default_args)
+{
+ LLXMLNodePtr root;
+ BOOL success = LLUICtrlFactory::getLayeredXMLNode(xml_filename, root);
+ if (!success)
+ {
+ llerrs << "Couldn't load string table" << llendl;
+ return false;
+ }
+
+ return LLTrans::parseStrings(root, default_args);
+}
+
+
+bool LLTransUtil::parseLanguageStrings(const std::string& xml_filename)
+{
+ LLXMLNodePtr root;
+ BOOL success = LLUICtrlFactory::getLayeredXMLNode(xml_filename, root);
+
+ if (!success)
+ {
+ llerrs << "Couldn't load string table " << xml_filename << llendl;
+ return false;
+ }
+
+ return LLTrans::parseLanguageStrings(root);
+}
diff --git a/indra/llui/lltransutil.h b/indra/llui/lltransutil.h
new file mode 100644
index 0000000000..9c7cee3f6f
--- /dev/null
+++ b/indra/llui/lltransutil.h
@@ -0,0 +1,45 @@
+/**
+ * @file lltransutil.h
+ * @brief LLTrans helper
+ *
+ * $LicenseInfo:firstyear=2000&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_TRANSUTIL_H
+#define LL_TRANSUTIL_H
+
+#include "lltrans.h"
+
+namespace LLTransUtil
+{
+ /**
+ * @brief Parses the xml file that holds the strings. Used once on startup
+ * @param xml_filename Filename to parse
+ * @param default_args Set of strings (expected to be in the file) to use as default replacement args, e.g. "SECOND_LIFE"
+ * @returns true if the file was parsed successfully, true if something went wrong
+ */
+ bool parseStrings(const std::string& xml_filename, const std::set<std::string>& default_args);
+
+ bool parseLanguageStrings(const std::string& xml_filename);
+};
+
+#endif
diff --git a/indra/llui/llui.cpp b/indra/llui/llui.cpp
index 3c9759695d..d33d8e3178 100644
--- a/indra/llui/llui.cpp
+++ b/indra/llui/llui.cpp
@@ -2,31 +2,25 @@
* @file llui.cpp
* @brief UI implementation
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -38,14 +32,13 @@
#include <map>
// Linden library includes
-#include "audioengine.h"
#include "v2math.h"
+#include "m3math.h"
#include "v4color.h"
#include "llrender.h"
#include "llrect.h"
-#include "llimagegl.h"
#include "lldir.h"
-#include "llfontgl.h"
+#include "llgl.h"
// Project includes
#include "llcontrol.h"
@@ -56,8 +49,15 @@
#include "llfloater.h"
#include "llfloaterreg.h"
#include "llmenugl.h"
+#include "llmenubutton.h"
+#include "llloadingindicator.h"
#include "llwindow.h"
+// for registration
+#include "llfiltereditor.h"
+#include "llflyoutbutton.h"
+#include "llsearcheditor.h"
+
// for XUIParse
#include "llquaternion.h"
#include <boost/tokenizer.hpp>
@@ -67,9 +67,6 @@
//
const LLColor4 UI_VERTEX_COLOR(1.f, 1.f, 1.f, 1.f);
-// Used to hide the flashing text cursor when window doesn't have focus.
-BOOL gShowTextEditCursor = TRUE;
-
// Language for UI construction
std::map<std::string, std::string> gTranslation;
std::list<std::string> gUntranslated;
@@ -78,12 +75,23 @@ std::list<std::string> gUntranslated;
/*static*/ LLUIAudioCallback LLUI::sAudioCallback = NULL;
/*static*/ LLVector2 LLUI::sGLScaleFactor(1.f, 1.f);
/*static*/ LLWindow* LLUI::sWindow = NULL;
-/*static*/ LLHtmlHelp* LLUI::sHtmlHelp = NULL;
/*static*/ LLView* LLUI::sRootView = NULL;
-/*static*/ BOOL LLUI::sShowXUINames = FALSE;
-/*static*/ std::stack<LLRect> LLScreenClipRect::sClipRectStack;
-
+/*static*/ BOOL LLUI::sDirty = FALSE;
+/*static*/ LLRect LLUI::sDirtyRect;
+/*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<LLLoadingIndicator> register_loading_indicator("loading_indicator");
//
@@ -173,19 +181,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();
}
@@ -196,7 +204,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);
@@ -416,7 +424,7 @@ void gl_corners_2d(S32 left, S32 top, S32 right, S32 bottom, S32 length, F32 max
}
-void gl_draw_image( S32 x, S32 y, LLImageGL* image, const LLColor4& color, const LLRectf& uv_rect )
+void gl_draw_image( S32 x, S32 y, LLTexture* image, const LLColor4& color, const LLRectf& uv_rect )
{
if (NULL == image)
{
@@ -426,7 +434,7 @@ void gl_draw_image( S32 x, S32 y, LLImageGL* image, const LLColor4& color, const
gl_draw_scaled_rotated_image( x, y, image->getWidth(0), image->getHeight(0), 0.f, image, color, uv_rect );
}
-void gl_draw_scaled_image(S32 x, S32 y, S32 width, S32 height, LLImageGL* image, const LLColor4& color, const LLRectf& uv_rect)
+void gl_draw_scaled_image(S32 x, S32 y, S32 width, S32 height, LLTexture* image, const LLColor4& color, const LLRectf& uv_rect)
{
if (NULL == image)
{
@@ -436,7 +444,7 @@ void gl_draw_scaled_image(S32 x, S32 y, S32 width, S32 height, LLImageGL* image,
gl_draw_scaled_rotated_image( x, y, width, height, 0.f, image, color, uv_rect );
}
-void gl_draw_scaled_image_with_border(S32 x, S32 y, S32 border_width, S32 border_height, S32 width, S32 height, LLImageGL* image, const LLColor4& color, BOOL solid_color, const LLRectf& uv_rect)
+void gl_draw_scaled_image_with_border(S32 x, S32 y, S32 border_width, S32 border_height, S32 width, S32 height, LLTexture* image, const LLColor4& color, BOOL solid_color, const LLRectf& uv_rect)
{
if (NULL == image)
{
@@ -452,7 +460,7 @@ void gl_draw_scaled_image_with_border(S32 x, S32 y, S32 border_width, S32 border
gl_draw_scaled_image_with_border(x, y, width, height, image, color, solid_color, uv_rect, scale_rect);
}
-void gl_draw_scaled_image_with_border(S32 x, S32 y, S32 width, S32 height, LLImageGL* image, const LLColor4& color, BOOL solid_color, const LLRectf& uv_rect, const LLRectf& scale_rect)
+void gl_draw_scaled_image_with_border(S32 x, S32 y, S32 width, S32 height, LLTexture* image, const LLColor4& color, BOOL solid_color, const LLRectf& uv_outer_rect, const LLRectf& center_rect)
{
stop_glerror();
@@ -462,36 +470,53 @@ void gl_draw_scaled_image_with_border(S32 x, S32 y, S32 width, S32 height, LLIma
return;
}
+ // add in offset of current image to current ui translation
+ const LLVector3 ui_scale = gGL.getUIScale();
+ const LLVector3 ui_translation = (gGL.getUITranslation() + LLVector3(x, y, 0.f)).scaledVec(ui_scale);
+
+ F32 uv_width = uv_outer_rect.getWidth();
+ F32 uv_height = uv_outer_rect.getHeight();
+
// shrink scaling region to be proportional to clipped image region
- LLRectf scale_rect_uv(
- uv_rect.mLeft + (scale_rect.mLeft * uv_rect.getWidth()),
- uv_rect.mBottom + (scale_rect.mTop * uv_rect.getHeight()),
- uv_rect.mLeft + (scale_rect.mRight * uv_rect.getWidth()),
- uv_rect.mBottom + (scale_rect.mBottom * uv_rect.getHeight()));
-
- S32 image_natural_width = llround((F32)image->getWidth(0) * uv_rect.getWidth());
- S32 image_natural_height = llround((F32)image->getHeight(0) * uv_rect.getHeight());
-
- LLRect draw_rect(0, height, width, 0);
- LLRect draw_scale_rect(llround(scale_rect_uv.mLeft * (F32)image->getWidth(0)),
- llround(scale_rect_uv.mTop * (F32)image->getHeight(0)),
- llround(scale_rect_uv.mRight * (F32)image->getWidth(0)),
- llround(scale_rect_uv.mBottom * (F32)image->getHeight(0)));
- // scale fixed region of image to drawn region
- draw_scale_rect.mRight += width - image_natural_width;
- draw_scale_rect.mTop += height - image_natural_height;
-
- S32 border_shrink_width = llmax(0, draw_scale_rect.mLeft - draw_scale_rect.mRight);
- S32 border_shrink_height = llmax(0, draw_scale_rect.mBottom - draw_scale_rect.mTop);
-
- F32 shrink_width_ratio = scale_rect.getWidth() == 1.f ? 0.f : border_shrink_width / ((F32)image_natural_width * (1.f - scale_rect.getWidth()));
- F32 shrink_height_ratio = scale_rect.getHeight() == 1.f ? 0.f : border_shrink_height / ((F32)image_natural_height * (1.f - scale_rect.getHeight()));
-
- F32 shrink_scale = 1.f - llmax(shrink_width_ratio, shrink_height_ratio);
- draw_scale_rect.mLeft = llround((F32)draw_scale_rect.mLeft * shrink_scale);
- draw_scale_rect.mTop = llround(lerp((F32)height, (F32)draw_scale_rect.mTop, shrink_scale));
- draw_scale_rect.mRight = llround(lerp((F32)width, (F32)draw_scale_rect.mRight, shrink_scale));
- draw_scale_rect.mBottom = llround((F32)draw_scale_rect.mBottom * shrink_scale);
+ LLRectf uv_center_rect(
+ uv_outer_rect.mLeft + (center_rect.mLeft * uv_width),
+ uv_outer_rect.mBottom + (center_rect.mTop * uv_height),
+ uv_outer_rect.mLeft + (center_rect.mRight * uv_width),
+ uv_outer_rect.mBottom + (center_rect.mBottom * uv_height));
+
+ F32 image_width = image->getWidth(0);
+ F32 image_height = image->getHeight(0);
+
+ S32 image_natural_width = llround(image_width * uv_width);
+ S32 image_natural_height = llround(image_height * uv_height);
+
+ LLRectf draw_center_rect( uv_center_rect.mLeft * image_width,
+ uv_center_rect.mTop * image_height,
+ uv_center_rect.mRight * image_width,
+ uv_center_rect.mBottom * image_height);
+
+ { // scale fixed region of image to drawn region
+ draw_center_rect.mRight += width - image_natural_width;
+ draw_center_rect.mTop += height - image_natural_height;
+
+ F32 border_shrink_width = llmax(0.f, draw_center_rect.mLeft - draw_center_rect.mRight);
+ F32 border_shrink_height = llmax(0.f, draw_center_rect.mBottom - draw_center_rect.mTop);
+
+ F32 shrink_width_ratio = center_rect.getWidth() == 1.f ? 0.f : border_shrink_width / ((F32)image_natural_width * (1.f - center_rect.getWidth()));
+ F32 shrink_height_ratio = center_rect.getHeight() == 1.f ? 0.f : border_shrink_height / ((F32)image_natural_height * (1.f - center_rect.getHeight()));
+
+ F32 shrink_scale = 1.f - llmax(shrink_width_ratio, shrink_height_ratio);
+
+ draw_center_rect.mLeft = llround(ui_translation.mV[VX] + (F32)draw_center_rect.mLeft * shrink_scale * ui_scale.mV[VX]);
+ draw_center_rect.mTop = llround(ui_translation.mV[VY] + lerp((F32)height, (F32)draw_center_rect.mTop, shrink_scale) * ui_scale.mV[VY]);
+ draw_center_rect.mRight = llround(ui_translation.mV[VX] + lerp((F32)width, (F32)draw_center_rect.mRight, shrink_scale) * ui_scale.mV[VX]);
+ draw_center_rect.mBottom = llround(ui_translation.mV[VY] + (F32)draw_center_rect.mBottom * shrink_scale * ui_scale.mV[VY]);
+ }
+
+ LLRectf draw_outer_rect(ui_translation.mV[VX],
+ ui_translation.mV[VY] + height * ui_scale.mV[VY],
+ ui_translation.mV[VX] + width * ui_scale.mV[VX],
+ ui_translation.mV[VY]);
LLGLSUIDefault gls_ui;
@@ -501,136 +526,174 @@ void gl_draw_scaled_image_with_border(S32 x, S32 y, S32 width, S32 height, LLIma
gGL.getTexUnit(0)->setTextureAlphaBlend(LLTexUnit::TBO_MULT, LLTexUnit::TBS_TEX_ALPHA, LLTexUnit::TBS_VERT_ALPHA);
}
- gGL.pushMatrix();
- {
- gGL.translatef((F32)x, (F32)y, 0.f);
+ gGL.getTexUnit(0)->bind(image);
- gGL.getTexUnit(0)->bind(image);
+ gGL.color4fv(color.mV);
+
+ const S32 NUM_VERTICES = 9 * 4; // 9 quads
+ LLVector2 uv[NUM_VERTICES];
+ LLVector3 pos[NUM_VERTICES];
- gGL.color4fv(color.mV);
-
- gGL.begin(LLRender::QUADS);
- {
- // draw bottom left
- gGL.texCoord2f(uv_rect.mLeft, uv_rect.mBottom);
- gGL.vertex2i(0, 0);
+ S32 index = 0;
- gGL.texCoord2f(scale_rect_uv.mLeft, uv_rect.mBottom);
- gGL.vertex2i(draw_scale_rect.mLeft, 0);
+ gGL.begin(LLRender::QUADS);
+ {
+ // draw bottom left
+ uv[index] = LLVector2(uv_outer_rect.mLeft, uv_outer_rect.mBottom);
+ pos[index] = LLVector3(draw_outer_rect.mLeft, draw_outer_rect.mBottom, 0.f);
+ index++;
- gGL.texCoord2f(scale_rect_uv.mLeft, scale_rect_uv.mBottom);
- gGL.vertex2i(draw_scale_rect.mLeft, draw_scale_rect.mBottom);
+ uv[index] = LLVector2(uv_center_rect.mLeft, uv_outer_rect.mBottom);
+ pos[index] = LLVector3(draw_center_rect.mLeft, draw_outer_rect.mBottom, 0.f);
+ index++;
- gGL.texCoord2f(uv_rect.mLeft, scale_rect_uv.mBottom);
- gGL.vertex2i(0, draw_scale_rect.mBottom);
+ uv[index] = LLVector2(uv_center_rect.mLeft, uv_center_rect.mBottom);
+ pos[index] = LLVector3(draw_center_rect.mLeft, draw_center_rect.mBottom, 0.f);
+ index++;
- // draw bottom middle
- gGL.texCoord2f(scale_rect_uv.mLeft, uv_rect.mBottom);
- gGL.vertex2i(draw_scale_rect.mLeft, 0);
+ uv[index] = LLVector2(uv_outer_rect.mLeft, uv_center_rect.mBottom);
+ pos[index] = LLVector3(draw_outer_rect.mLeft, draw_center_rect.mBottom, 0.f);
+ index++;
- gGL.texCoord2f(scale_rect_uv.mRight, uv_rect.mBottom);
- gGL.vertex2i(draw_scale_rect.mRight, 0);
+ // draw bottom middle
+ uv[index] = LLVector2(uv_center_rect.mLeft, uv_outer_rect.mBottom);
+ pos[index] = LLVector3(draw_center_rect.mLeft, draw_outer_rect.mBottom, 0.f);
+ index++;
- gGL.texCoord2f(scale_rect_uv.mRight, scale_rect_uv.mBottom);
- gGL.vertex2i(draw_scale_rect.mRight, draw_scale_rect.mBottom);
+ uv[index] = LLVector2(uv_center_rect.mRight, uv_outer_rect.mBottom);
+ pos[index] = LLVector3(draw_center_rect.mRight, draw_outer_rect.mBottom, 0.f);
+ index++;
- gGL.texCoord2f(scale_rect_uv.mLeft, scale_rect_uv.mBottom);
- gGL.vertex2i(draw_scale_rect.mLeft, draw_scale_rect.mBottom);
+ uv[index] = LLVector2(uv_center_rect.mRight, uv_center_rect.mBottom);
+ pos[index] = LLVector3(draw_center_rect.mRight, draw_center_rect.mBottom, 0.f);
+ index++;
- // draw bottom right
- gGL.texCoord2f(scale_rect_uv.mRight, uv_rect.mBottom);
- gGL.vertex2i(draw_scale_rect.mRight, 0);
+ uv[index] = LLVector2(uv_center_rect.mLeft, uv_center_rect.mBottom);
+ pos[index] = LLVector3(draw_center_rect.mLeft, draw_center_rect.mBottom, 0.f);
+ index++;
- gGL.texCoord2f(uv_rect.mRight, uv_rect.mBottom);
- gGL.vertex2i(width, 0);
+ // draw bottom right
+ uv[index] = LLVector2(uv_center_rect.mRight, uv_outer_rect.mBottom);
+ pos[index] = LLVector3(draw_center_rect.mRight, draw_outer_rect.mBottom, 0.f);
+ index++;
- gGL.texCoord2f(uv_rect.mRight, scale_rect_uv.mBottom);
- gGL.vertex2i(width, draw_scale_rect.mBottom);
+ uv[index] = LLVector2(uv_outer_rect.mRight, uv_outer_rect.mBottom);
+ pos[index] = LLVector3(draw_outer_rect.mRight, draw_outer_rect.mBottom, 0.f);
+ index++;
- gGL.texCoord2f(scale_rect_uv.mRight, scale_rect_uv.mBottom);
- gGL.vertex2i(draw_scale_rect.mRight, draw_scale_rect.mBottom);
+ uv[index] = LLVector2(uv_outer_rect.mRight, uv_center_rect.mBottom);
+ pos[index] = LLVector3(draw_outer_rect.mRight, draw_center_rect.mBottom, 0.f);
+ index++;
- // draw left
- gGL.texCoord2f(uv_rect.mLeft, scale_rect_uv.mBottom);
- gGL.vertex2i(0, draw_scale_rect.mBottom);
+ uv[index] = LLVector2(uv_center_rect.mRight, uv_center_rect.mBottom);
+ pos[index] = LLVector3(draw_center_rect.mRight, draw_center_rect.mBottom, 0.f);
+ index++;
- gGL.texCoord2f(scale_rect_uv.mLeft, scale_rect_uv.mBottom);
- gGL.vertex2i(draw_scale_rect.mLeft, draw_scale_rect.mBottom);
+ // draw left
+ uv[index] = LLVector2(uv_outer_rect.mLeft, uv_center_rect.mBottom);
+ pos[index] = LLVector3(draw_outer_rect.mLeft, draw_center_rect.mBottom, 0.f);
+ index++;
- gGL.texCoord2f(scale_rect_uv.mLeft, scale_rect_uv.mTop);
- gGL.vertex2i(draw_scale_rect.mLeft, draw_scale_rect.mTop);
+ uv[index] = LLVector2(uv_center_rect.mLeft, uv_center_rect.mBottom);
+ pos[index] = LLVector3(draw_center_rect.mLeft, draw_center_rect.mBottom, 0.f);
+ index++;
- gGL.texCoord2f(uv_rect.mLeft, scale_rect_uv.mTop);
- gGL.vertex2i(0, draw_scale_rect.mTop);
+ uv[index] = LLVector2(uv_center_rect.mLeft, uv_center_rect.mTop);
+ pos[index] = LLVector3(draw_center_rect.mLeft, draw_center_rect.mTop, 0.f);
+ index++;
- // draw middle
- gGL.texCoord2f(scale_rect_uv.mLeft, scale_rect_uv.mBottom);
- gGL.vertex2i(draw_scale_rect.mLeft, draw_scale_rect.mBottom);
+ uv[index] = LLVector2(uv_outer_rect.mLeft, uv_center_rect.mTop);
+ pos[index] = LLVector3(draw_outer_rect.mLeft, draw_center_rect.mTop, 0.f);
+ index++;
- gGL.texCoord2f(scale_rect_uv.mRight, scale_rect_uv.mBottom);
- gGL.vertex2i(draw_scale_rect.mRight, draw_scale_rect.mBottom);
+ // draw middle
+ uv[index] = LLVector2(uv_center_rect.mLeft, uv_center_rect.mBottom);
+ pos[index] = LLVector3(draw_center_rect.mLeft, draw_center_rect.mBottom, 0.f);
+ index++;
- gGL.texCoord2f(scale_rect_uv.mRight, scale_rect_uv.mTop);
- gGL.vertex2i(draw_scale_rect.mRight, draw_scale_rect.mTop);
+ uv[index] = LLVector2(uv_center_rect.mRight, uv_center_rect.mBottom);
+ pos[index] = LLVector3(draw_center_rect.mRight, draw_center_rect.mBottom, 0.f);
+ index++;
- gGL.texCoord2f(scale_rect_uv.mLeft, scale_rect_uv.mTop);
- gGL.vertex2i(draw_scale_rect.mLeft, draw_scale_rect.mTop);
+ uv[index] = LLVector2(uv_center_rect.mRight, uv_center_rect.mTop);
+ pos[index] = LLVector3(draw_center_rect.mRight, draw_center_rect.mTop, 0.f);
+ index++;
- // draw right
- gGL.texCoord2f(scale_rect_uv.mRight, scale_rect_uv.mBottom);
- gGL.vertex2i(draw_scale_rect.mRight, draw_scale_rect.mBottom);
+ uv[index] = LLVector2(uv_center_rect.mLeft, uv_center_rect.mTop);
+ pos[index] = LLVector3(draw_center_rect.mLeft, draw_center_rect.mTop, 0.f);
+ index++;
+
+ // draw right
+ uv[index] = LLVector2(uv_center_rect.mRight, uv_center_rect.mBottom);
+ pos[index] = LLVector3(draw_center_rect.mRight, draw_center_rect.mBottom, 0.f);
+ index++;
- gGL.texCoord2f(uv_rect.mRight, scale_rect_uv.mBottom);
- gGL.vertex2i(width, draw_scale_rect.mBottom);
+ uv[index] = LLVector2(uv_outer_rect.mRight, uv_center_rect.mBottom);
+ pos[index] = LLVector3(draw_outer_rect.mRight, draw_center_rect.mBottom, 0.f);
+ index++;
- gGL.texCoord2f(uv_rect.mRight, scale_rect_uv.mTop);
- gGL.vertex2i(width, draw_scale_rect.mTop);
+ uv[index] = LLVector2(uv_outer_rect.mRight, uv_center_rect.mTop);
+ pos[index] = LLVector3(draw_outer_rect.mRight, draw_center_rect.mTop, 0.f);
+ index++;
- gGL.texCoord2f(scale_rect_uv.mRight, scale_rect_uv.mTop);
- gGL.vertex2i(draw_scale_rect.mRight, draw_scale_rect.mTop);
+ uv[index] = LLVector2(uv_center_rect.mRight, uv_center_rect.mTop);
+ pos[index] = LLVector3(draw_center_rect.mRight, draw_center_rect.mTop, 0.f);
+ index++;
- // draw top left
- gGL.texCoord2f(uv_rect.mLeft, scale_rect_uv.mTop);
- gGL.vertex2i(0, draw_scale_rect.mTop);
+ // draw top left
+ uv[index] = LLVector2(uv_outer_rect.mLeft, uv_center_rect.mTop);
+ pos[index] = LLVector3(draw_outer_rect.mLeft, draw_center_rect.mTop, 0.f);
+ index++;
- gGL.texCoord2f(scale_rect_uv.mLeft, scale_rect_uv.mTop);
- gGL.vertex2i(draw_scale_rect.mLeft, draw_scale_rect.mTop);
+ uv[index] = LLVector2(uv_center_rect.mLeft, uv_center_rect.mTop);
+ pos[index] = LLVector3(draw_center_rect.mLeft, draw_center_rect.mTop, 0.f);
+ index++;
- gGL.texCoord2f(scale_rect_uv.mLeft, uv_rect.mTop);
- gGL.vertex2i(draw_scale_rect.mLeft, height);
+ uv[index] = LLVector2(uv_center_rect.mLeft, uv_outer_rect.mTop);
+ pos[index] = LLVector3(draw_center_rect.mLeft, draw_outer_rect.mTop, 0.f);
+ index++;
- gGL.texCoord2f(uv_rect.mLeft, uv_rect.mTop);
- gGL.vertex2i(0, height);
+ uv[index] = LLVector2(uv_outer_rect.mLeft, uv_outer_rect.mTop);
+ pos[index] = LLVector3(draw_outer_rect.mLeft, draw_outer_rect.mTop, 0.f);
+ index++;
- // draw top middle
- gGL.texCoord2f(scale_rect_uv.mLeft, scale_rect_uv.mTop);
- gGL.vertex2i(draw_scale_rect.mLeft, draw_scale_rect.mTop);
+ // draw top middle
+ uv[index] = LLVector2(uv_center_rect.mLeft, uv_center_rect.mTop);
+ pos[index] = LLVector3(draw_center_rect.mLeft, draw_center_rect.mTop, 0.f);
+ index++;
- gGL.texCoord2f(scale_rect_uv.mRight, scale_rect_uv.mTop);
- gGL.vertex2i(draw_scale_rect.mRight, draw_scale_rect.mTop);
+ uv[index] = LLVector2(uv_center_rect.mRight, uv_center_rect.mTop);
+ pos[index] = LLVector3(draw_center_rect.mRight, draw_center_rect.mTop, 0.f);
+ index++;
- gGL.texCoord2f(scale_rect_uv.mRight, uv_rect.mTop);
- gGL.vertex2i(draw_scale_rect.mRight, height);
+ uv[index] = LLVector2(uv_center_rect.mRight, uv_outer_rect.mTop);
+ pos[index] = LLVector3(draw_center_rect.mRight, draw_outer_rect.mTop, 0.f);
+ index++;
- gGL.texCoord2f(scale_rect_uv.mLeft, uv_rect.mTop);
- gGL.vertex2i(draw_scale_rect.mLeft, height);
+ uv[index] = LLVector2(uv_center_rect.mLeft, uv_outer_rect.mTop);
+ pos[index] = LLVector3(draw_center_rect.mLeft, draw_outer_rect.mTop, 0.f);
+ index++;
- // draw top right
- gGL.texCoord2f(scale_rect_uv.mRight, scale_rect_uv.mTop);
- gGL.vertex2i(draw_scale_rect.mRight, draw_scale_rect.mTop);
+ // draw top right
+ uv[index] = LLVector2(uv_center_rect.mRight, uv_center_rect.mTop);
+ pos[index] = LLVector3(draw_center_rect.mRight, draw_center_rect.mTop, 0.f);
+ index++;
- gGL.texCoord2f(uv_rect.mRight, scale_rect_uv.mTop);
- gGL.vertex2i(width, draw_scale_rect.mTop);
+ uv[index] = LLVector2(uv_outer_rect.mRight, uv_center_rect.mTop);
+ pos[index] = LLVector3(draw_outer_rect.mRight, draw_center_rect.mTop, 0.f);
+ index++;
- gGL.texCoord2f(uv_rect.mRight, uv_rect.mTop);
- gGL.vertex2i(width, height);
+ uv[index] = LLVector2(uv_outer_rect.mRight, uv_outer_rect.mTop);
+ pos[index] = LLVector3(draw_outer_rect.mRight, draw_outer_rect.mTop, 0.f);
+ index++;
- gGL.texCoord2f(scale_rect_uv.mRight, uv_rect.mTop);
- gGL.vertex2i(draw_scale_rect.mRight, height);
- }
- gGL.end();
+ uv[index] = LLVector2(uv_center_rect.mRight, uv_outer_rect.mTop);
+ pos[index] = LLVector3(draw_center_rect.mRight, draw_outer_rect.mTop, 0.f);
+ index++;
+
+ gGL.vertexBatchPreTransformed(pos, uv, NUM_VERTICES);
}
- gGL.popMatrix();
+ gGL.end();
if (solid_color)
{
@@ -638,12 +701,12 @@ void gl_draw_scaled_image_with_border(S32 x, S32 y, S32 width, S32 height, LLIma
}
}
-void gl_draw_rotated_image(S32 x, S32 y, F32 degrees, LLImageGL* image, const LLColor4& color, const LLRectf& uv_rect)
+void gl_draw_rotated_image(S32 x, S32 y, F32 degrees, LLTexture* image, const LLColor4& color, const LLRectf& uv_rect)
{
gl_draw_scaled_rotated_image( x, y, image->getWidth(0), image->getHeight(0), degrees, image, color, uv_rect );
}
-void gl_draw_scaled_rotated_image(S32 x, S32 y, S32 width, S32 height, F32 degrees, LLImageGL* image, const LLColor4& color, const LLRectf& uv_rect)
+void gl_draw_scaled_rotated_image(S32 x, S32 y, S32 width, S32 height, F32 degrees, LLTexture* image, const LLColor4& color, const LLRectf& uv_rect)
{
if (NULL == image)
{
@@ -653,77 +716,87 @@ void gl_draw_scaled_rotated_image(S32 x, S32 y, S32 width, S32 height, F32 degre
LLGLSUIDefault gls_ui;
- gGL.pushMatrix();
- {
- gGL.translatef((F32)x, (F32)y, 0.f);
- if( degrees )
- {
- 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.getTexUnit(0)->bind(image);
- gGL.color4fv(color.mV);
-
- gGL.begin(LLRender::QUADS);
- {
- gGL.texCoord2f(uv_rect.mRight, uv_rect.mTop);
- gGL.vertex2i(width, height );
+ gGL.getTexUnit(0)->bind(image);
- gGL.texCoord2f(uv_rect.mLeft, uv_rect.mTop);
- gGL.vertex2i(0, height );
+ gGL.color4fv(color.mV);
- gGL.texCoord2f(uv_rect.mLeft, uv_rect.mBottom);
- gGL.vertex2i(0, 0);
+ if (degrees == 0.f)
+ {
+ const S32 NUM_VERTICES = 4; // 9 quads
+ LLVector2 uv[NUM_VERTICES];
+ LLVector3 pos[NUM_VERTICES];
- gGL.texCoord2f(uv_rect.mRight, uv_rect.mBottom);
- gGL.vertex2i(width, 0);
+ gGL.begin(LLRender::QUADS);
+ {
+ LLVector3 ui_scale = gGL.getUIScale();
+ LLVector3 ui_translation = gGL.getUITranslation();
+ ui_translation.mV[VX] += x;
+ ui_translation.mV[VY] += y;
+ ui_translation.scaleVec(ui_scale);
+ S32 index = 0;
+ S32 scaled_width = llround(width * ui_scale.mV[VX]);
+ S32 scaled_height = llround(height * ui_scale.mV[VY]);
+
+ uv[index] = LLVector2(uv_rect.mRight, uv_rect.mTop);
+ pos[index] = LLVector3(ui_translation.mV[VX] + scaled_width, ui_translation.mV[VY] + scaled_height, 0.f);
+ index++;
+
+ uv[index] = LLVector2(uv_rect.mLeft, uv_rect.mTop);
+ pos[index] = LLVector3(ui_translation.mV[VX], ui_translation.mV[VY] + scaled_height, 0.f);
+ index++;
+
+ uv[index] = LLVector2(uv_rect.mLeft, uv_rect.mBottom);
+ pos[index] = LLVector3(ui_translation.mV[VX], ui_translation.mV[VY], 0.f);
+ index++;
+
+ uv[index] = LLVector2(uv_rect.mRight, uv_rect.mBottom);
+ pos[index] = LLVector3(ui_translation.mV[VX] + scaled_width, ui_translation.mV[VY], 0.f);
+ index++;
+
+ gGL.vertexBatchPreTransformed(pos, uv, NUM_VERTICES);
}
gGL.end();
}
- gGL.popMatrix();
-}
-
-
-void gl_draw_scaled_image_inverted(S32 x, S32 y, S32 width, S32 height, LLImageGL* image, const LLColor4& color, const LLRectf& uv_rect)
-{
- if (NULL == image)
+ else
{
- llwarns << "image == NULL; aborting function" << llendl;
- return;
- }
-
- LLGLSUIDefault gls_ui;
+ gGL.pushUIMatrix();
+ gGL.translateUI((F32)x, (F32)y, 0.f);
+
+ F32 offset_x = F32(width/2);
+ F32 offset_y = F32(height/2);
- gGL.pushMatrix();
- {
- gGL.translatef((F32)x, (F32)y, 0.f);
+ 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);
{
- gGL.texCoord2f(uv_rect.mRight, uv_rect.mBottom);
- gGL.vertex2i(width, height );
+ LLVector3 v;
- gGL.texCoord2f(uv_rect.mLeft, uv_rect.mBottom);
- gGL.vertex2i(0, height );
+ v = LLVector3(offset_x, offset_y, 0.f) * quat;
+ gGL.texCoord2f(uv_rect.mRight, uv_rect.mTop);
+ 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, 0);
+ gGL.vertex2f(v.mV[0], v.mV[1] );
- gGL.texCoord2f(uv_rect.mRight, uv_rect.mTop);
- gGL.vertex2i(width, 0);
+ v = LLVector3(-offset_x, -offset_y, 0.f) * quat;
+ gGL.texCoord2f(uv_rect.mLeft, uv_rect.mBottom);
+ 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.vertex2f(v.mV[0], v.mV[1] );
}
gGL.end();
+ gGL.popUIMatrix();
}
- gGL.popMatrix();
}
@@ -752,25 +825,6 @@ void gl_stippled_line_3d( const LLVector3& start, const LLVector3& end, const LL
LLUI::setLineWidth(1.f);
}
-
-void gl_rect_2d_xor(S32 left, S32 top, S32 right, S32 bottom)
-{
- gGL.color4fv( LLColor4::white.mV );
- glLogicOp( GL_XOR );
- stop_glerror();
-
- gGL.begin(LLRender::QUADS);
- gGL.vertex2i(left, top);
- gGL.vertex2i(left, bottom);
- gGL.vertex2i(right, bottom);
- gGL.vertex2i(right, top);
- gGL.end();
-
- glLogicOp( GL_COPY );
- stop_glerror();
-}
-
-
void gl_arc_2d(F32 center_x, F32 center_y, F32 radius, S32 steps, BOOL filled, F32 start_angle, F32 end_angle)
{
if (end_angle < start_angle)
@@ -778,9 +832,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;
@@ -811,15 +865,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;
@@ -850,7 +904,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)
@@ -877,9 +931,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);
@@ -888,11 +942,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
@@ -1004,42 +1058,6 @@ void gl_washer_segment_2d(F32 outer_radius, F32 inner_radius, F32 start_radians,
gGL.end();
}
-// Draws spokes around a circle.
-void gl_washer_spokes_2d(F32 outer_radius, F32 inner_radius, S32 count, const LLColor4& inner_color, const LLColor4& outer_color)
-{
- const F32 DELTA = F_TWO_PI / count;
- const F32 HALF_DELTA = DELTA * 0.5f;
- const F32 SIN_DELTA = sin( DELTA );
- const F32 COS_DELTA = cos( DELTA );
-
- F32 x1 = outer_radius * cos( HALF_DELTA );
- F32 y1 = outer_radius * sin( HALF_DELTA );
- F32 x2 = inner_radius * cos( HALF_DELTA );
- F32 y2 = inner_radius * sin( HALF_DELTA );
-
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-
- gGL.begin( LLRender::LINES );
- {
- while( count-- )
- {
- gGL.color4fv(outer_color.mV);
- gGL.vertex2f( x1, y1 );
- gGL.color4fv(inner_color.mV);
- gGL.vertex2f( x2, y2 );
-
- F32 x1_new = x1 * COS_DELTA - y1 * SIN_DELTA;
- y1 = x1 * SIN_DELTA + y1 * COS_DELTA;
- x1 = x1_new;
-
- F32 x2_new = x2 * COS_DELTA - y2 * SIN_DELTA;
- y2 = x2 * SIN_DELTA + y2 * COS_DELTA;
- x2 = x2_new;
- }
- }
- gGL.end();
-}
-
void gl_rect_2d_simple_tex( S32 width, S32 height )
{
gGL.begin( LLRender::QUADS );
@@ -1081,9 +1099,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)
@@ -1224,9 +1242,10 @@ void gl_segmented_rect_2d_tex(const S32 left,
}
gGL.end();
- gGL.popMatrix();
+ gGL.popUIMatrix();
}
+//FIXME: rewrite to use scissor?
void gl_segmented_rect_2d_fragment_tex(const S32 left,
const S32 top,
const S32 right,
@@ -1241,9 +1260,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)
@@ -1414,7 +1433,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,
@@ -1556,12 +1575,6 @@ void gl_segmented_rect_3d_tex_top(const LLVector2& border_scale, const LLVector3
gl_segmented_rect_3d_tex(border_scale, border_width, border_height, width_vec, height_vec, ROUNDED_RECT_TOP);
}
-bool handleShowXUINamesChanged(const LLSD& newvalue)
-{
- LLUI::sShowXUINames = newvalue.asBoolean();
- return true;
-}
-
void LLUI::initClass(const settings_map_t& settings,
LLImageProviderInterface* image_provider,
LLUIAudioCallback audio_callback,
@@ -1571,7 +1584,6 @@ void LLUI::initClass(const settings_map_t& settings,
sSettingGroups = settings;
if ((get_ptr_in_map(sSettingGroups, std::string("config")) == NULL) ||
- (get_ptr_in_map(sSettingGroups, std::string("color")) == NULL) ||
(get_ptr_in_map(sSettingGroups, std::string("floater")) == NULL) ||
(get_ptr_in_map(sSettingGroups, std::string("ignores")) == NULL))
{
@@ -1582,12 +1594,8 @@ void LLUI::initClass(const settings_map_t& settings,
sAudioCallback = audio_callback;
sGLScaleFactor = (scale_factor == NULL) ? LLVector2(1.f, 1.f) : *scale_factor;
sWindow = NULL; // set later in startup
- LLFontGL::sShadowColor = LLUI::sSettingGroups["color"]->getColor("ColorDropShadow");
+ LLFontGL::sShadowColor = LLUIColorTable::instance().getColor("ColorDropShadow");
- static LLUICachedControl<bool> show_xui_names ("ShowXUINames", false);
- LLUI::sShowXUINames = show_xui_names;
- LLUI::sSettingGroups["config"]->getControl("ShowXUINames")->getSignal()->connect(boost::bind(&handleShowXUINamesChanged, _2));
-
// Callbacks for associating controls with floater visibilty:
LLUICtrl::CommitCallbackRegistry::defaultRegistrar().add("Floater.Toggle", boost::bind(&LLFloaterReg::toggleFloaterInstance, _2));
LLUICtrl::CommitCallbackRegistry::defaultRegistrar().add("Floater.Show", boost::bind(&LLFloaterReg::showFloaterInstance, _2));
@@ -1597,6 +1605,12 @@ void LLUI::initClass(const settings_map_t& settings,
// Button initialization callback for toggle buttons
LLUICtrl::CommitCallbackRegistry::defaultRegistrar().add("Button.SetFloaterToggle", boost::bind(&LLButton::setFloaterToggle, _1, _2));
+ // Button initialization callback for toggle buttons on dockale floaters
+ LLUICtrl::CommitCallbackRegistry::defaultRegistrar().add("Button.SetDockableFloaterToggle", boost::bind(&LLButton::setDockableFloaterToggle, _1, _2));
+
+ // Display the help topic for the current context
+ LLUICtrl::CommitCallbackRegistry::defaultRegistrar().add("Button.ShowHelp", boost::bind(&LLButton::showHelp, _1, _2));
+
// Currently unused, but kept for reference:
LLUICtrl::CommitCallbackRegistry::defaultRegistrar().add("Button.ToggleFloater", boost::bind(&LLButton::toggleFloaterAndSetToggleState, _1, _2));
@@ -1609,11 +1623,32 @@ 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)
+{
+ if (!sDirty)
+ {
+ sDirtyRect = rect;
+ sDirty = TRUE;
+ }
+ else
+ {
+ sDirtyRect.unionWith(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;
@@ -1622,14 +1657,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();
}
@@ -1637,7 +1672,7 @@ void LLUI::popMatrix()
//static
void LLUI::loadIdentity()
{
- glLoadIdentity();
+ gGL.loadUIIdentity();
LLFontGL::sCurOrigin.mX = 0;
LLFontGL::sCurOrigin.mY = 0;
LLFontGL::sCurOrigin.mZ = 0;
@@ -1657,7 +1692,7 @@ void LLUI::setLineWidth(F32 width)
}
//static
-void LLUI::setCursorPositionScreen(S32 x, S32 y)
+void LLUI::setMousePositionScreen(S32 x, S32 y)
{
S32 screen_x, screen_y;
screen_x = llround((F32)x * sGLScaleFactor.mV[VX]);
@@ -1670,26 +1705,34 @@ void LLUI::setCursorPositionScreen(S32 x, S32 y)
}
//static
-void LLUI::setCursorPositionLocal(const LLView* viewp, S32 x, S32 y)
+void LLUI::getMousePositionScreen(S32 *x, S32 *y)
+{
+ LLCoordWindow cursor_pos_window;
+ getWindow()->getCursorPosition(&cursor_pos_window);
+ LLCoordGL cursor_pos_gl;
+ getWindow()->convertCoords(cursor_pos_window, &cursor_pos_gl);
+ *x = llround((F32)cursor_pos_gl.mX / sGLScaleFactor.mV[VX]);
+ *y = llround((F32)cursor_pos_gl.mY / sGLScaleFactor.mV[VX]);
+}
+
+//static
+void LLUI::setMousePositionLocal(const LLView* viewp, S32 x, S32 y)
{
S32 screen_x, screen_y;
viewp->localPointToScreen(x, y, &screen_x, &screen_y);
- setCursorPositionScreen(screen_x, screen_y);
+ setMousePositionScreen(screen_x, screen_y);
}
//static
-void LLUI::getCursorPositionLocal(const LLView* viewp, S32 *x, S32 *y)
+void LLUI::getMousePositionLocal(const LLView* viewp, S32 *x, S32 *y)
{
- LLCoordWindow cursor_pos_window;
- LLView::getWindow()->getCursorPosition(&cursor_pos_window);
- LLCoordGL cursor_pos_gl;
- LLView::getWindow()->convertCoords(cursor_pos_window, &cursor_pos_gl);
- cursor_pos_gl.mX = llround((F32)cursor_pos_gl.mX / LLUI::sGLScaleFactor.mV[VX]);
- cursor_pos_gl.mY = llround((F32)cursor_pos_gl.mY / LLUI::sGLScaleFactor.mV[VY]);
- viewp->screenPointToLocal(cursor_pos_gl.mX, cursor_pos_gl.mY, x, y);
+ S32 screen_x, screen_y;
+ getMousePositionScreen(&screen_x, &screen_y);
+ viewp->screenPointToLocal(screen_x, screen_y, x, y);
}
+
// On Windows, the user typically sets the language when they install the
// app (by running it with a shortcut that sets InstallLanguage). On Mac,
// or on Windows if the SecondLife.exe executable is run directly, the
@@ -1718,6 +1761,33 @@ std::string LLUI::getLanguage()
return language;
}
+struct SubDir : public LLInitParam::Block<SubDir>
+{
+ Mandatory<std::string> value;
+
+ SubDir()
+ : value("value")
+ {}
+};
+
+struct Directory : public LLInitParam::Block<Directory>
+{
+ Multiple<SubDir, AtLeast<1> > subdirs;
+
+ Directory()
+ : subdirs("subdir")
+ {}
+};
+
+struct Paths : public LLInitParam::Block<Paths>
+{
+ Multiple<Directory, AtLeast<1> > directories;
+
+ Paths()
+ : directories("directory")
+ {}
+};
+
//static
void LLUI::setupPaths()
{
@@ -1725,21 +1795,36 @@ void LLUI::setupPaths()
LLXMLNodePtr root;
BOOL success = LLXMLNode::parseFile(filename, root, NULL);
+ Paths paths;
+ LLXUIParser::instance().readXUI(root, paths, filename);
+
sXUIPaths.clear();
- if (success)
+ if (success && paths.validateBlock())
{
LLStringUtil::format_map_t path_args;
path_args["[LANGUAGE]"] = LLUI::getLanguage();
- for (LLXMLNodePtr path = root->getFirstChild(); path.notNull(); path = path->getNextSibling())
+ for (LLInitParam::ParamIterator<Directory>::const_iterator it = paths.directories().begin(),
+ end_it = paths.directories().end();
+ it != end_it;
+ ++it)
{
- std::string path_val_ui(path->getValue());
+ std::string path_val_ui;
+ for (LLInitParam::ParamIterator<SubDir>::const_iterator subdir_it = it->subdirs().begin(),
+ subdir_end_it = it->subdirs().end();
+ subdir_it != subdir_end_it;)
+ {
+ path_val_ui += subdir_it->value();
+ if (++subdir_it != subdir_end_it)
+ path_val_ui += gDirUtilp->getDirDelimiter();
+ }
LLStringUtil::format(path_val_ui, path_args);
if (std::find(sXUIPaths.begin(), sXUIPaths.end(), path_val_ui) == sXUIPaths.end())
{
sXUIPaths.push_back(path_val_ui);
}
+
}
}
else // parsing failed
@@ -1820,11 +1905,11 @@ void LLUI::glRectToScreen(const LLRect& gl, LLRect *screen)
}
//static
-LLPointer<LLUIImage> LLUI::getUIImageByID(const LLUUID& image_id)
+LLPointer<LLUIImage> LLUI::getUIImageByID(const LLUUID& image_id, S32 priority)
{
if (sImageProvider)
{
- return sImageProvider->getUIImageByID(image_id);
+ return sImageProvider->getUIImageByID(image_id, priority);
}
else
{
@@ -1833,127 +1918,139 @@ LLPointer<LLUIImage> LLUI::getUIImageByID(const LLUUID& image_id)
}
//static
-LLPointer<LLUIImage> LLUI::getUIImage(const std::string& name)
+LLPointer<LLUIImage> LLUI::getUIImage(const std::string& name, S32 priority)
{
if (!name.empty() && sImageProvider)
- return sImageProvider->getUIImage(name);
+ return sImageProvider->getUIImage(name, priority);
else
return NULL;
}
-// static
-void LLUI::setHtmlHelp(LLHtmlHelp* html_help)
-{
- LLUI::sHtmlHelp = html_help;
-}
-
-// static
-boost::function<const LLColor4&()> LLUI::getCachedColorFunctor(const std::string& color_name)
-{
- return LLCachedControl<LLColor4>(*sSettingGroups["color"], color_name, LLColor4::magenta);
-}
-
-// static
LLControlGroup& LLUI::getControlControlGroup (const std::string& controlname)
{
for (settings_map_t::iterator itor = sSettingGroups.begin();
itor != sSettingGroups.end(); ++itor)
{
- if(itor->second!= NULL)
+ LLControlGroup* control_group = itor->second;
+ if(control_group != NULL)
{
- if (sSettingGroups[(itor->first)]->controlExists(controlname))
- return *sSettingGroups[(itor->first)];
+ if (control_group->controlExists(controlname))
+ return *control_group;
}
}
return *sSettingGroups["config"]; // default group
}
-LLScreenClipRect::LLScreenClipRect(const LLRect& rect, BOOL enabled) : mScissorState(GL_SCISSOR_TEST), mEnabled(enabled)
+//static
+void LLUI::addPopup(LLView* viewp)
{
- if (mEnabled)
+ if (sAddPopupFunc)
{
- pushClipRect(rect);
+ sAddPopupFunc(viewp);
}
- mScissorState.setEnabled(!sClipRectStack.empty());
- updateScissorRegion();
}
-LLScreenClipRect::~LLScreenClipRect()
+//static
+void LLUI::removePopup(LLView* viewp)
{
- if (mEnabled)
+ if (sRemovePopupFunc)
{
- popClipRect();
+ sRemovePopupFunc(viewp);
}
- updateScissorRegion();
}
-//static
-void LLScreenClipRect::pushClipRect(const LLRect& rect)
+//static
+void LLUI::clearPopups()
{
- LLRect combined_clip_rect = rect;
- if (!sClipRectStack.empty())
+ if (sClearPopupsFunc)
{
- LLRect top = sClipRectStack.top();
- combined_clip_rect.intersectWith(top);
+ sClearPopupsFunc();
}
- sClipRectStack.push(combined_clip_rect);
}
-//static
-void LLScreenClipRect::popClipRect()
+//static
+void LLUI::reportBadKeystroke()
{
- sClipRectStack.pop();
+ make_ui_sound("UISndBadKeystroke");
}
-
+
//static
-void LLScreenClipRect::updateScissorRegion()
+// 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)
{
- if (sClipRectStack.empty()) return;
+ const S32 CURSOR_HEIGHT = 16; // Approximate "normal" cursor size
+ const S32 CURSOR_WIDTH = 8;
- LLRect rect = sClipRectStack.top();
- stop_glerror();
- S32 x,y,w,h;
- x = llfloor(rect.mLeft * LLUI::sGLScaleFactor.mV[VX]);
- y = llfloor(rect.mBottom * LLUI::sGLScaleFactor.mV[VY]);
- w = llmax(0, llceil(rect.getWidth() * LLUI::sGLScaleFactor.mV[VX])) + 1;
- h = llmax(0, llceil(rect.getHeight() * LLUI::sGLScaleFactor.mV[VY])) + 1;
- glScissor( x,y,w,h );
- stop_glerror();
-}
+ LLView* parent = view->getParent();
+ S32 mouse_x;
+ S32 mouse_y;
+ LLUI::getMousePositionScreen(&mouse_x, &mouse_y);
-LLLocalClipRect::LLLocalClipRect(const LLRect &rect, BOOL enabled)
-: LLScreenClipRect(LLRect(rect.mLeft + LLFontGL::sCurOrigin.mX,
- rect.mTop + LLFontGL::sCurOrigin.mY,
- rect.mRight + LLFontGL::sCurOrigin.mX,
- rect.mBottom + LLFontGL::sCurOrigin.mY),
- enabled)
-{
+ // If no spawn location provided, use mouse position
+ if (spawn_x == S32_MAX || spawn_y == S32_MAX)
+ {
+ spawn_x = mouse_x + CURSOR_WIDTH;
+ spawn_y = mouse_y - CURSOR_HEIGHT;
+ }
+
+ LLRect virtual_window_rect = parent->getLocalRect();
+
+ LLRect mouse_rect;
+ const S32 MOUSE_CURSOR_PADDING = 1;
+ mouse_rect.setLeftTopAndSize(mouse_x - MOUSE_CURSOR_PADDING,
+ mouse_y + MOUSE_CURSOR_PADDING,
+ CURSOR_WIDTH + MOUSE_CURSOR_PADDING * 2,
+ CURSOR_HEIGHT + MOUSE_CURSOR_PADDING * 2);
+
+ S32 local_x, local_y;
+ // convert screen coordinates to tooltipview-local coordinates
+ parent->screenPointToLocal(spawn_x, spawn_y, &local_x, &local_y);
+
+ // Start at spawn position (using left/top)
+ view->setOrigin( local_x, local_y - view->getRect().getHeight());
+ // Make sure we're onscreen and not overlapping the mouse
+ view->translateIntoRectWithExclusion( virtual_window_rect, mouse_rect, FALSE );
}
+
+// LLLocalClipRect and LLScreenClipRect moved to lllocalcliprect.h/cpp
+
namespace LLInitParam
{
- TypedParam<LLUIColor >::TypedParam(BlockDescriptor& descriptor, const char* name, const LLUIColor& value, ParamDescriptor::validation_func_t func)
- : super_t(descriptor, name, value, func),
+ TypedParam<LLUIColor >::TypedParam(BlockDescriptor& descriptor, const char* name, const LLUIColor& value, ParamDescriptor::validation_func_t func, S32 min_count, S32 max_count)
+ : super_t(descriptor, name, value, func, min_count, max_count),
red("red"),
green("green"),
blue("blue"),
alpha("alpha"),
control("")
- {}
+ {
+ setBlockFromValue();
+ }
- LLUIColor TypedParam<LLUIColor>::getValueFromBlock() const
+ void TypedParam<LLUIColor>::setValueFromBlock() const
{
if (control.isProvided())
{
- return LLUIColorTable::instance().getColor(control);
+ mData.mValue = LLUIColorTable::instance().getColor(control);
}
else
{
- return LLColor4(red, green, blue, alpha);
+ mData.mValue = LLColor4(red, green, blue, alpha);
}
}
+
+ void TypedParam<LLUIColor>::setBlockFromValue()
+ {
+ LLColor4 color = mData.mValue.get();
+ 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);
+ }
void TypeValues<LLUIColor>::declareValues()
{
@@ -1964,48 +2061,65 @@ namespace LLInitParam
declare("blue", LLColor4::blue);
}
- TypedParam<const LLFontGL*>::TypedParam(BlockDescriptor& descriptor, const char* name, const LLFontGL*const value, ParamDescriptor::validation_func_t func)
- : super_t(descriptor, name, value, func),
- name("", std::string("")),
- size("size", std::string("")),
- style("style", std::string(""))
- {}
+ bool ParamCompare<const LLFontGL*, false>::equals(const LLFontGL* a, const LLFontGL* b)
+ {
+ return !(a->getFontDesc() < b->getFontDesc())
+ && !(b->getFontDesc() < a->getFontDesc());
+ }
- const LLFontGL* TypedParam<const LLFontGL*>::getValueFromBlock() const
+ TypedParam<const LLFontGL*>::TypedParam(BlockDescriptor& descriptor, const char* _name, const LLFontGL*const value, ParamDescriptor::validation_func_t func, S32 min_count, S32 max_count)
+ : super_t(descriptor, _name, value, func, min_count, max_count),
+ name("name"),
+ size("size"),
+ style("style")
{
- if (name.isProvided())
- {
- const LLFontGL* res_fontp = LLFontGL::getFontByName(name);
- if (res_fontp)
- {
- return res_fontp;
- }
+ setBlockFromValue();
+ addSynonym(name, "");
+ setBlockFromValue();
+ }
- U8 fontstyle = 0;
- fontstyle = LLFontGL::getStyleFromString(style());
- LLFontDescriptor desc(name(), size(), fontstyle);
- const LLFontGL* fontp = LLFontGL::getFont(desc);
- if (fontp)
- {
- return fontp;
- }
+ void TypedParam<const LLFontGL*>::setValueFromBlock() const
+ {
+ const LLFontGL* res_fontp = LLFontGL::getFontByName(name);
+ if (res_fontp)
+ {
+ mData.mValue = res_fontp;
+ return;
}
- // default to current value
- return mData.mValue;
+ U8 fontstyle = 0;
+ fontstyle = LLFontGL::getStyleFromString(style());
+ LLFontDescriptor desc(name(), size(), fontstyle);
+ const LLFontGL* fontp = LLFontGL::getFont(desc);
+ if (fontp)
+ {
+ mData.mValue = fontp;
+ }
+ }
+
+ void TypedParam<const LLFontGL*>::setBlockFromValue()
+ {
+ if (mData.mValue)
+ {
+ name.set(LLFontGL::nameFromFont(mData.mValue), false);
+ size.set(LLFontGL::sizeFromFont(mData.mValue), false);
+ style.set(LLFontGL::getStringFromStyle(mData.mValue->getFontDesc().getStyle()), false);
+ }
}
- TypedParam<LLRect>::TypedParam(BlockDescriptor& descriptor, const char* name, const LLRect& value, ParamDescriptor::validation_func_t func)
- : super_t(descriptor, name, value, func),
+ TypedParam<LLRect>::TypedParam(BlockDescriptor& descriptor, const char* name, const LLRect& value, ParamDescriptor::validation_func_t func, S32 min_count, S32 max_count)
+ : super_t(descriptor, name, value, func, min_count, max_count),
left("left"),
top("top"),
right("right"),
bottom("bottom"),
width("width"),
height("height")
- {}
+ {
+ setBlockFromValue();
+ }
- LLRect TypedParam<LLRect>::getValueFromBlock() const
+ void TypedParam<LLRect>::setValueFromBlock() const
{
LLRect rect;
@@ -2066,9 +2180,43 @@ namespace LLInitParam
rect.mBottom = bottom;
rect.mTop = top;
}
- return rect;
+ mData.mValue = rect;
+ }
+
+ void TypedParam<LLRect>::setBlockFromValue()
+ {
+ // because of the ambiguity in specifying a rect by position and/or dimensions
+ // we clear the "provided" flag so that values from xui/etc have priority
+ // over those calculated from the rect object
+
+ left.set(mData.mValue.mLeft, false);
+ right.set(mData.mValue.mRight, false);
+ bottom.set(mData.mValue.mBottom, false);
+ top.set(mData.mValue.mTop, false);
+ width.set(mData.mValue.getWidth(), false);
+ height.set(mData.mValue.getHeight(), false);
+ }
+
+ TypedParam<LLCoordGL>::TypedParam(BlockDescriptor& descriptor, const char* name, LLCoordGL value, ParamDescriptor::validation_func_t func, S32 min_count, S32 max_count)
+ : super_t(descriptor, name, value, func, min_count, max_count),
+ x("x"),
+ y("y")
+ {
+ setBlockFromValue();
}
+ void TypedParam<LLCoordGL>::setValueFromBlock() const
+ {
+ mData.mValue.set(x, y);
+ }
+
+ void TypedParam<LLCoordGL>::setBlockFromValue()
+ {
+ x.set(mData.mValue.mX, false);
+ y.set(mData.mValue.mY, false);
+ }
+
+
void TypeValues<LLFontGL::HAlign>::declareValues()
{
declare("left", LLFontGL::LEFT);
diff --git a/indra/llui/llui.h b/indra/llui/llui.h
index 35c0bb478e..fc545c85d5 100644
--- a/indra/llui/llui.h
+++ b/indra/llui/llui.h
@@ -2,31 +2,25 @@
* @file llui.h
* @brief GL function declarations and other general static UI services.
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -39,14 +33,16 @@
#include "llrect.h"
#include "llcontrol.h"
#include "llcoord.h"
-//#include "llhtmlhelp.h"
-#include "llgl.h" // *TODO: break this dependency
-#include <stack>
#include "lluiimage.h" // *TODO: break this dependency, need to add #include "lluiimage.h" to all widgets that hold an Optional<LLUIImage*> in their paramblocks
#include "llinitparam.h"
#include "llregistry.h"
+#include "lluicolor.h"
+#include "lluicolortable.h"
#include <boost/signals2.hpp>
#include "lllazyvalue.h"
+#include "llhandle.h" // *TODO: remove this dependency, added as a
+ // convenience when LLHandle moved to llhandle.h
+#include "llframetimer.h"
// LLUIFactory
#include "llsd.h"
@@ -55,14 +51,13 @@
#include "llfontgl.h"
class LLColor4;
-class LLHtmlHelp;
-class LLImageGL;
class LLVector3;
class LLVector2;
class LLUIImage;
class LLUUID;
class LLWindow;
class LLView;
+class LLHelp;
// UI colors
extern const LLColor4 UI_VERTEX_COLOR;
@@ -95,18 +90,14 @@ void gl_ring( F32 radius, F32 width, const LLColor4& center_color, const LLColor
void gl_corners_2d(S32 left, S32 top, S32 right, S32 bottom, S32 length, F32 max_frac);
void gl_washer_2d(F32 outer_radius, F32 inner_radius, S32 steps, const LLColor4& inner_color, const LLColor4& outer_color);
void gl_washer_segment_2d(F32 outer_radius, F32 inner_radius, F32 start_radians, F32 end_radians, S32 steps, const LLColor4& inner_color, const LLColor4& outer_color);
-void gl_washer_spokes_2d(F32 outer_radius, F32 inner_radius, S32 count, const LLColor4& inner_color, const LLColor4& outer_color);
-
-void gl_draw_image(S32 x, S32 y, LLImageGL* image, const LLColor4& color = UI_VERTEX_COLOR, const LLRectf& uv_rect = LLRectf(0.f, 1.f, 1.f, 0.f));
-void gl_draw_scaled_image(S32 x, S32 y, S32 width, S32 height, LLImageGL* image, const LLColor4& color = UI_VERTEX_COLOR, const LLRectf& uv_rect = LLRectf(0.f, 1.f, 1.f, 0.f));
-void gl_draw_rotated_image(S32 x, S32 y, F32 degrees, LLImageGL* image, const LLColor4& color = UI_VERTEX_COLOR, const LLRectf& uv_rect = LLRectf(0.f, 1.f, 1.f, 0.f));
-void gl_draw_scaled_rotated_image(S32 x, S32 y, S32 width, S32 height, F32 degrees,LLImageGL* image, const LLColor4& color = UI_VERTEX_COLOR, const LLRectf& uv_rect = LLRectf(0.f, 1.f, 1.f, 0.f));
-void gl_draw_scaled_image_with_border(S32 x, S32 y, S32 border_width, S32 border_height, S32 width, S32 height, LLImageGL* image, const LLColor4 &color, BOOL solid_color = FALSE, const LLRectf& uv_rect = LLRectf(0.f, 1.f, 1.f, 0.f));
-void gl_draw_scaled_image_with_border(S32 x, S32 y, S32 width, S32 height, LLImageGL* image, const LLColor4 &color, BOOL solid_color = FALSE, const LLRectf& uv_rect = LLRectf(0.f, 1.f, 1.f, 0.f), const LLRectf& scale_rect = LLRectf(0.f, 1.f, 1.f, 0.f));
-// Flip vertical, used for LLFloaterHTML
-void gl_draw_scaled_image_inverted(S32 x, S32 y, S32 width, S32 height, LLImageGL* image, const LLColor4& color = UI_VERTEX_COLOR, const LLRectf& uv_rect = LLRectf(0.f, 1.f, 1.f, 0.f));
-
-void gl_rect_2d_xor(S32 left, S32 top, S32 right, S32 bottom);
+
+void gl_draw_image(S32 x, S32 y, LLTexture* image, const LLColor4& color = UI_VERTEX_COLOR, const LLRectf& uv_rect = LLRectf(0.f, 1.f, 1.f, 0.f));
+void gl_draw_scaled_image(S32 x, S32 y, S32 width, S32 height, LLTexture* image, const LLColor4& color = UI_VERTEX_COLOR, const LLRectf& uv_rect = LLRectf(0.f, 1.f, 1.f, 0.f));
+void gl_draw_rotated_image(S32 x, S32 y, F32 degrees, LLTexture* image, const LLColor4& color = UI_VERTEX_COLOR, const LLRectf& uv_rect = LLRectf(0.f, 1.f, 1.f, 0.f));
+void gl_draw_scaled_rotated_image(S32 x, S32 y, S32 width, S32 height, F32 degrees,LLTexture* image, const LLColor4& color = UI_VERTEX_COLOR, const LLRectf& uv_rect = LLRectf(0.f, 1.f, 1.f, 0.f));
+void gl_draw_scaled_image_with_border(S32 x, S32 y, S32 border_width, S32 border_height, S32 width, S32 height, LLTexture* image, const LLColor4 &color, BOOL solid_color = FALSE, const LLRectf& uv_rect = LLRectf(0.f, 1.f, 1.f, 0.f));
+void gl_draw_scaled_image_with_border(S32 x, S32 y, S32 width, S32 height, LLTexture* image, const LLColor4 &color, BOOL solid_color = FALSE, const LLRectf& uv_rect = LLRectf(0.f, 1.f, 1.f, 0.f), const LLRectf& scale_rect = LLRectf(0.f, 1.f, 1.f, 0.f));
+
void gl_stippled_line_3d( const LLVector3& start, const LLVector3& end, const LLColor4& color, F32 phase = 0.f );
void gl_rect_2d_simple_tex( S32 width, S32 height );
@@ -149,9 +140,6 @@ inline void gl_rect_2d_offset_local( const LLRect& rect, S32 pixel_offset, BOOL
gl_rect_2d_offset_local( rect.mLeft, rect.mTop, rect.mRight, rect.mBottom, pixel_offset, filled );
}
-// Used to hide the flashing text cursor when window doesn't have focus.
-extern BOOL gShowTextEditCursor;
-
class LLImageProviderInterface;
typedef void (*LLUIAudioCallback)(const LLUUID& uuid);
@@ -164,18 +152,27 @@ 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();
static void loadIdentity();
static void translate(F32 x, F32 y, F32 z = 0.0f);
+ static LLRect sDirtyRect;
+ static BOOL sDirty;
+ static void dirtyRect(LLRect rect);
+
// Return the ISO639 language name ("en", "ko", etc.) for the viewer UI.
// http://www.loc.gov/standards/iso639-2/php/code_list.php
static std::string getLanguage();
@@ -189,23 +186,37 @@ public:
static LLView* getRootView() { return sRootView; }
static void setRootView(LLView* view) { sRootView = view; }
static std::string locateSkin(const std::string& filename);
- static void setCursorPositionScreen(S32 x, S32 y);
- static void setCursorPositionLocal(const LLView* viewp, S32 x, S32 y);
- static void getCursorPositionLocal(const LLView* viewp, S32 *x, S32 *y);
+ static void setMousePositionScreen(S32 x, S32 y);
+ static void getMousePositionScreen(S32 *x, S32 *y);
+ static void setMousePositionLocal(const LLView* viewp, S32 x, S32 y);
+ static void getMousePositionLocal(const LLView* viewp, S32 *x, S32 *y);
static void setScaleFactor(const LLVector2& scale_factor);
static void setLineWidth(F32 width);
- static LLPointer<LLUIImage> getUIImageByID(const LLUUID& image_id);
- static LLPointer<LLUIImage> getUIImage(const std::string& name);
+ static LLPointer<LLUIImage> getUIImageByID(const LLUUID& image_id, S32 priority = 0);
+ static LLPointer<LLUIImage> getUIImage(const std::string& name, S32 priority = 0);
static LLVector2 getWindowSize();
static void screenPointToGL(S32 screen_x, S32 screen_y, S32 *gl_x, S32 *gl_y);
static void glPointToScreen(S32 gl_x, S32 gl_y, S32 *screen_x, S32 *screen_y);
static void screenRectToGL(const LLRect& screen, LLRect *gl);
static void glRectToScreen(const LLRect& gl, LLRect *screen);
- static void setHtmlHelp(LLHtmlHelp* html_help);
- static boost::function<const LLColor4&()> getCachedColorFunctor(const std::string& color_name);
// Returns the control group containing the control name, or the default group
static LLControlGroup& getControlControlGroup (const std::string& controlname);
-
+ static F32 getMouseIdleTime() { return sMouseIdleTimer.getElapsedTimeF32(); }
+ 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
+ // view's parent.
+ static void positionViewNearMouse(LLView* view, S32 spawn_x = S32_MAX, S32 spawn_y = S32_MAX);
+
//
// Data
//
@@ -213,354 +224,21 @@ public:
static LLUIAudioCallback sAudioCallback;
static LLVector2 sGLScaleFactor;
static LLWindow* sWindow;
- static BOOL sShowXUINames;
- static LLHtmlHelp* sHtmlHelp;
static LLView* sRootView;
+ static LLHelp* sHelpImpl;
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;
};
-// FactoryPolicy is a static class that controls the creation and lookup of UI elements,
-// such as floaters.
-// The key parameter is used to provide a unique identifier and/or associated construction
-// parameters for a given UI instance
-//
-// Specialize this traits for different types, or provide a class with an identical interface
-// in the place of the traits parameter
-//
-// For example:
-//
-// template <>
-// class FactoryPolicy<MyClass> /* FactoryPolicy specialized for MyClass */
-// {
-// public:
-// static MyClass* findInstance(const LLSD& key = LLSD())
-// {
-// /* return instance of MyClass associated with key */
-// }
-//
-// static MyClass* createInstance(const LLSD& key = LLSD())
-// {
-// /* create new instance of MyClass using key for construction parameters */
-// }
-// }
-//
-// class MyClass : public LLUIFactory<MyClass>
-// {
-// /* uses FactoryPolicy<MyClass> by default */
-// }
-
-template <class T>
-class FactoryPolicy
-{
-public:
- // basic factory methods
- static T* findInstance(const LLSD& key); // unimplemented, provide specialiation
- static T* createInstance(const LLSD& key); // unimplemented, provide specialiation
-};
-
-// VisibilityPolicy controls the visibility of UI elements, such as floaters.
-// The key parameter is used to store the unique identifier of a given UI instance
-//
-// Specialize this traits for different types, or duplicate this interface for specific instances
-// (see above)
-
-template <class T>
-class VisibilityPolicy
-{
-public:
- // visibility methods
- static bool visible(T* instance, const LLSD& key); // unimplemented, provide specialiation
- static void show(T* instance, const LLSD& key); // unimplemented, provide specialiation
- static void hide(T* instance, const LLSD& key); // unimplemented, provide specialiation
-};
-
-// Manages generation of UI elements by LLSD, such that (generally) there is
-// a unique instance per distinct LLSD parameter
-// Class T is the instance type being managed, and the FACTORY_POLICY and VISIBILITY_POLICY
-// classes provide static methods for creating, accessing, showing and hiding the associated
-// element T
-template <class T, class FACTORY_POLICY = FactoryPolicy<T>, class VISIBILITY_POLICY = VisibilityPolicy<T> >
-class LLUIFactory
-{
-public:
- // give names to the template parameters so derived classes can refer to them
- // except this doesn't work in gcc
- typedef FACTORY_POLICY factory_policy_t;
- typedef VISIBILITY_POLICY visibility_policy_t;
-
- LLUIFactory()
- {
- }
-
- virtual ~LLUIFactory()
- {
- }
-
- // default show and hide methods
- static T* showInstance(const LLSD& key = LLSD())
- {
- T* instance = getInstance(key);
- if (instance != NULL)
- {
- VISIBILITY_POLICY::show(instance, key);
- }
- return instance;
- }
-
- static void hideInstance(const LLSD& key = LLSD())
- {
- T* instance = getInstance(key);
- if (instance != NULL)
- {
- VISIBILITY_POLICY::hide(instance, key);
- }
- }
-
- static void toggleInstance(const LLSD& key = LLSD())
- {
- if (instanceVisible(key))
- {
- hideInstance(key);
- }
- else
- {
- showInstance(key);
- }
- }
-
- static bool instanceVisible(const LLSD& key = LLSD())
- {
- T* instance = FACTORY_POLICY::findInstance(key);
- return instance != NULL && VISIBILITY_POLICY::visible(instance, key);
- }
-
- static T* getInstance(const LLSD& key = LLSD())
- {
- T* instance = FACTORY_POLICY::findInstance(key);
- if (instance == NULL)
- {
- instance = FACTORY_POLICY::createInstance(key);
- }
- return instance;
- }
-
-};
-
-
-// Creates a UI singleton by ignoring the identifying parameter
-// and always generating the same instance via the LLUIFactory interface.
-// Note that since UI elements can be destroyed by their hierarchy, this singleton
-// pattern uses a static pointer to an instance that will be re-created as needed.
-//
-// Usage Pattern:
-//
-// class LLFloaterFoo : public LLFloater, public LLUISingleton<LLFloaterFoo>
-// {
-// friend class LLUISingleton<LLFloaterFoo>;
-// private:
-// LLFloaterFoo(const LLSD& key);
-// };
-//
-// Note that LLUISingleton takes an option VisibilityPolicy parameter that defines
-// how showInstance(), hideInstance(), etc. work.
-//
-// https://wiki.lindenlab.com/mediawiki/index.php?title=LLUISingleton&oldid=79352
-
-template <class T, class VISIBILITY_POLICY = VisibilityPolicy<T> >
-class LLUISingleton: public LLUIFactory<T, LLUISingleton<T, VISIBILITY_POLICY>, VISIBILITY_POLICY>
-{
-protected:
-
- // T must derive from LLUISingleton<T>
- LLUISingleton() { sInstance = static_cast<T*>(this); }
- ~LLUISingleton() { sInstance = NULL; }
-
-public:
- static T* findInstance(const LLSD& key = LLSD())
- {
- return sInstance;
- }
-
- static T* createInstance(const LLSD& key = LLSD())
- {
- if (sInstance == NULL)
- {
- sInstance = new T(key);
- }
- return sInstance;
- }
-
- static void destroyInstance()
- {
- delete sInstance;
- sInstance = NULL;
- }
-
-private:
- static T* sInstance;
-};
-
-template <class T, class U> T* LLUISingleton<T,U>::sInstance = NULL;
-
-class LLScreenClipRect
-{
-public:
- LLScreenClipRect(const LLRect& rect, BOOL enabled = TRUE);
- virtual ~LLScreenClipRect();
-
-private:
- static void pushClipRect(const LLRect& rect);
- static void popClipRect();
- static void updateScissorRegion();
-
-private:
- LLGLState mScissorState;
- BOOL mEnabled;
-
- static std::stack<LLRect> sClipRectStack;
-};
-
-class LLLocalClipRect : public LLScreenClipRect
-{
-public:
- LLLocalClipRect(const LLRect& rect, BOOL enabled = TRUE);
-};
-
-template <typename T>
-class LLTombStone : public LLRefCount
-{
-public:
- LLTombStone(T* target = NULL) : mTarget(target) {}
-
- void setTarget(T* target) { mTarget = target; }
- T* getTarget() const { return mTarget; }
-private:
- T* mTarget;
-};
-
-// LLHandles are used to refer to objects whose lifetime you do not control or influence.
-// Calling get() on a handle will return a pointer to the referenced object or NULL,
-// if the object no longer exists. Note that during the lifetime of the returned pointer,
-// you are assuming that the object will not be deleted by any action you perform,
-// or any other thread, as normal when using pointers, so avoid using that pointer outside of
-// the local code block.
-//
-// https://wiki.lindenlab.com/mediawiki/index.php?title=LLHandle&oldid=79669
-
-template <typename T>
-class LLHandle
-{
-public:
- LLHandle() : mTombStone(sDefaultTombStone) {}
- const LLHandle<T>& operator =(const LLHandle<T>& other)
- {
- mTombStone = other.mTombStone;
- return *this;
- }
-
- bool isDead() const
- {
- return mTombStone->getTarget() == NULL;
- }
-
- void markDead()
- {
- mTombStone = sDefaultTombStone;
- }
-
- T* get() const
- {
- return mTombStone->getTarget();
- }
-
- friend bool operator== (const LLHandle<T>& lhs, const LLHandle<T>& rhs)
- {
- return lhs.mTombStone == rhs.mTombStone;
- }
- friend bool operator!= (const LLHandle<T>& lhs, const LLHandle<T>& rhs)
- {
- return !(lhs == rhs);
- }
- friend bool operator< (const LLHandle<T>& lhs, const LLHandle<T>& rhs)
- {
- return lhs.mTombStone < rhs.mTombStone;
- }
- friend bool operator> (const LLHandle<T>& lhs, const LLHandle<T>& rhs)
- {
- return lhs.mTombStone > rhs.mTombStone;
- }
-protected:
-
-protected:
- LLPointer<LLTombStone<T> > mTombStone;
-
-private:
- static LLPointer<LLTombStone<T> > sDefaultTombStone;
-};
-
-// initialize static "empty" tombstone pointer
-template <typename T> LLPointer<LLTombStone<T> > LLHandle<T>::sDefaultTombStone = new LLTombStone<T>();
-
-
-template <typename T>
-class LLRootHandle : public LLHandle<T>
-{
-public:
- LLRootHandle(T* object) { bind(object); }
- LLRootHandle() {};
- ~LLRootHandle() { unbind(); }
-
- // this is redundant, since a LLRootHandle *is* an LLHandle
- LLHandle<T> getHandle() { return LLHandle<T>(*this); }
-
- void bind(T* object)
- {
- // unbind existing tombstone
- if (LLHandle<T>::mTombStone.notNull())
- {
- if (LLHandle<T>::mTombStone->getTarget() == object) return;
- LLHandle<T>::mTombStone->setTarget(NULL);
- }
- // tombstone reference counted, so no paired delete
- LLHandle<T>::mTombStone = new LLTombStone<T>(object);
- }
-
- void unbind()
- {
- LLHandle<T>::mTombStone->setTarget(NULL);
- }
- //don't allow copying of root handles, since there should only be one
-private:
- LLRootHandle(const LLRootHandle& other) {};
-};
-
-// Use this as a mixin for simple classes that need handles and when you don't
-// want handles at multiple points of the inheritance hierarchy
-template <typename T>
-class LLHandleProvider
-{
-protected:
- typedef LLHandle<T> handle_type_t;
- LLHandleProvider()
- {
- // provided here to enforce T deriving from LLHandleProvider<T>
- }
-
- LLHandle<T> getHandle()
- {
- // perform lazy binding to avoid small tombstone allocations for handle
- // providers whose handles are never referenced
- mHandle.bind(static_cast<T*>(this));
- return mHandle;
- }
-
-private:
- LLRootHandle<T> mHandle;
-};
+// Moved LLLocalClipRect to lllocalcliprect.h
+// Moved all LLHandle-related code to llhandle.h
//RN: maybe this needs to moved elsewhere?
class LLImageProviderInterface
@@ -569,8 +247,8 @@ protected:
LLImageProviderInterface() {};
virtual ~LLImageProviderInterface() {};
public:
- virtual LLPointer<LLUIImage> getUIImage(const std::string& name) = 0;
- virtual LLPointer<LLUIImage> getUIImageByID(const LLUUID& id) = 0;
+ virtual LLPointer<LLUIImage> getUIImage(const std::string& name, S32 priority) = 0;
+ virtual LLPointer<LLUIImage> getUIImageByID(const LLUUID& id, S32 priority) = 0;
virtual void cleanUp() = 0;
};
@@ -665,8 +343,8 @@ template <typename T> LLRegisterWith<LLDestroyClassList> LLDestroyClass<T>::sReg
// useful parameter blocks
struct TimeIntervalParam : public LLInitParam::Choice<TimeIntervalParam>
{
- Option<F32> seconds;
- Option<S32> frames;
+ Alternative<F32> seconds;
+ Alternative<S32> frames;
TimeIntervalParam()
: seconds("seconds"),
frames("frames")
@@ -690,8 +368,6 @@ public:
{}
};
-typedef LLLazyValue<LLColor4> LLUIColor;
-
namespace LLInitParam
{
template<>
@@ -707,9 +383,10 @@ namespace LLInitParam
width,
height;
- TypedParam(BlockDescriptor& descriptor, const char* name, const LLRect& value, ParamDescriptor::validation_func_t func);
+ TypedParam(BlockDescriptor& descriptor, const char* name, const LLRect& value, ParamDescriptor::validation_func_t func, S32 min_count, S32 max_count);
- LLRect getValueFromBlock() const;
+ void setValueFromBlock() const;
+ void setBlockFromValue();
};
template<>
@@ -724,28 +401,44 @@ namespace LLInitParam
{
typedef BlockValue<LLUIColor> super_t;
public:
- Optional<F32> red;
- Optional<F32> green;
- Optional<F32> blue;
- Optional<F32> alpha;
+ Optional<F32> red,
+ green,
+ blue,
+ alpha;
Optional<std::string> control;
- TypedParam(BlockDescriptor& descriptor, const char* name, const LLUIColor& value, ParamDescriptor::validation_func_t func);
- LLUIColor getValueFromBlock() const;
+ TypedParam(BlockDescriptor& descriptor, const char* name, const LLUIColor& value, ParamDescriptor::validation_func_t func, S32 min_count, S32 max_count);
+ void setValueFromBlock() const;
+ void setBlockFromValue();
};
+ // provide a better default for Optional<const LLFontGL*> than NULL
+ template <>
+ struct DefaultInitializer<const LLFontGL*>
+ {
+ // return reference to a single default instance of T
+ // built-in types will be initialized to zero, default constructor otherwise
+ static const LLFontGL* get()
+ {
+ static const LLFontGL* sDefaultFont = LLFontGL::getFontDefault();
+ return sDefaultFont;
+ }
+ };
+
+
template<>
class TypedParam<const LLFontGL*>
: public BlockValue<const LLFontGL*>
{
typedef BlockValue<const LLFontGL*> super_t;
public:
- Optional<std::string> name;
- Optional<std::string> size;
- Optional<std::string> style;
+ Optional<std::string> name,
+ size,
+ style;
- TypedParam(BlockDescriptor& descriptor, const char* name, const LLFontGL* const value, ParamDescriptor::validation_func_t func);
- const LLFontGL* getValueFromBlock() const;
+ TypedParam(BlockDescriptor& descriptor, const char* name, const LLFontGL* const value, ParamDescriptor::validation_func_t func, S32 min_count, S32 max_count);
+ void setValueFromBlock() const;
+ void setBlockFromValue();
};
template<>
@@ -765,13 +458,27 @@ namespace LLInitParam
{
static void declareValues();
};
-}
-namespace LLInitParam
-{
- template<>
- bool ParamCompare<LLLazyValue<LLColor4> >::equals(
- const LLLazyValue<LLColor4> &a, const LLLazyValue<LLColor4> &b);
+ template<>
+ struct ParamCompare<const LLFontGL*, false>
+ {
+ static bool equals(const LLFontGL* a, const LLFontGL* b);
+ };
+
+
+ template<>
+ class TypedParam<LLCoordGL>
+ : public BlockValue<LLCoordGL>
+ {
+ typedef BlockValue<LLCoordGL> super_t;
+ public:
+ Optional<S32> x,
+ y;
+
+ TypedParam(BlockDescriptor& descriptor, const char* name, LLCoordGL value, ParamDescriptor::validation_func_t func, S32 min_count, S32 max_count);
+ void setValueFromBlock() const;
+ void setBlockFromValue();
+ };
}
#endif
diff --git a/indra/llui/lluicolortable.cpp b/indra/llui/lluicolortable.cpp
index 27ba6cc8b4..9891e38f7b 100644
--- a/indra/llui/lluicolortable.cpp
+++ b/indra/llui/lluicolortable.cpp
@@ -2,8 +2,25 @@
* @file lluicolortable.cpp
* @brief brief LLUIColorTable class implementation file
*
- * $LicenseInfo:firstyear=2009&license=viewergpl$
- * Copyright (c) 2009, Linden Research, Inc.
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -11,7 +28,10 @@
#include <queue>
+#include "lldir.h"
+#include "llui.h"
#include "lluicolortable.h"
+#include "lluictrlfactory.h"
LLUIColorTable::ColorParams::ColorParams()
: value("value"),
@@ -26,17 +46,16 @@ LLUIColorTable::ColorEntryParams::ColorEntryParams()
}
LLUIColorTable::Params::Params()
-: color_entries("color_entries")
+: color_entries("color")
{
}
-void LLUIColorTable::init(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;
string_string_map_t unresolved_refs;
- mColors.clear();
for(LLInitParam::ParamIterator<ColorEntryParams>::const_iterator it = p.color_entries().begin();
it != p.color_entries().end();
++it)
@@ -44,7 +63,7 @@ void LLUIColorTable::init(const Params& p)
ColorEntryParams color_entry = *it;
if(color_entry.color.value.isChosen())
{
- mColors.insert(string_color_map_t::value_type(color_entry.name, color_entry.color.value));
+ setColor(color_entry.name, color_entry.color.value, table);
}
else
{
@@ -66,19 +85,21 @@ void LLUIColorTable::init(const Params& p)
// we haven't visited any references yet
visited_refs.clear();
- string_string_map_t::iterator it = unresolved_refs.begin();
+ string_string_map_t::iterator current = unresolved_refs.begin();
+ string_string_map_t::iterator previous;
+
while(true)
{
- if(it != unresolved_refs.end())
+ if(current != unresolved_refs.end())
{
// locate the current reference in the previously visited references...
- string_color_ref_iter_map_t::iterator visited = visited_refs.lower_bound(it->first);
+ string_color_ref_iter_map_t::iterator visited = visited_refs.lower_bound(current->first);
if(visited != visited_refs.end()
- && !(visited_refs.key_comp()(it->first, visited->first)))
+ && !(visited_refs.key_comp()(current->first, visited->first)))
{
// ...if we find the current reference in the previously visited references
// we know that there is a cycle
- std::string ending_ref = it->first;
+ std::string ending_ref = current->first;
std::string warning("The following colors form a cycle: ");
// warn about the references in the chain and remove them from
@@ -102,17 +123,17 @@ void LLUIColorTable::init(const Params& p)
else
{
// ...continue along the reference chain
- ref_chain.push(it->first);
- visited_refs.insert(visited, string_color_ref_iter_map_t::value_type(it->first, it));
+ ref_chain.push(current->first);
+ visited_refs.insert(visited, string_color_ref_iter_map_t::value_type(current->first, current));
}
}
else
{
// since this reference does not refer to another reference it must refer to an
// actual color, lets find it...
- string_color_map_t::iterator color_value = mColors.find(it->second);
+ string_color_map_t::iterator color_value = mLoadedColors.find(previous->second);
- if(color_value != mColors.end())
+ if(color_value != mLoadedColors.end())
{
// ...we found the color, and we now add every reference in the reference chain
// to the color map
@@ -120,7 +141,7 @@ void LLUIColorTable::init(const Params& p)
iter != visited_refs.end();
++iter)
{
- mColors.insert(string_color_map_t::value_type(iter->first, color_value->second));
+ setColor(iter->first, color_value->second, mLoadedColors);
unresolved_refs.erase(iter->second);
}
@@ -143,13 +164,164 @@ void LLUIColorTable::init(const Params& p)
}
// find the next color reference in the reference chain
- it = unresolved_refs.find(it->second);
+ previous = current;
+ current = unresolved_refs.find(current->second);
+ }
+ }
+}
+
+void LLUIColorTable::clear()
+{
+ clearTable(mLoadedColors);
+ clearTable(mUserSetColors);
+}
+
+LLUIColor LLUIColorTable::getColor(const std::string& name, const LLColor4& default_color) const
+{
+ string_color_map_t::const_iterator iter = mUserSetColors.find(name);
+
+ if(iter != mUserSetColors.end())
+ {
+ return LLUIColor(&iter->second);
+ }
+
+ iter = mLoadedColors.find(name);
+
+ if(iter != mLoadedColors.end())
+ {
+ return LLUIColor(&iter->second);
+ }
+
+ return LLUIColor(default_color);
+}
+
+// update user color, loaded colors are parsed on initialization
+void LLUIColorTable::setColor(const std::string& name, const LLColor4& color)
+{
+ setColor(name, color, mUserSetColors);
+ setColor(name, color, mLoadedColors);
+}
+
+bool LLUIColorTable::loadFromSettings()
+{
+ bool result = false;
+
+ std::string default_filename = gDirUtilp->getExpandedFilename(LL_PATH_DEFAULT_SKIN, "colors.xml");
+ 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, mLoadedColors);
+ }
+
+ std::string user_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "colors.xml");
+ loadFromFilename(user_filename, mUserSetColors);
+
+ return result;
+}
+
+void LLUIColorTable::saveUserSettings() const
+{
+ Params params;
+
+ for(string_color_map_t::const_iterator it = mUserSetColors.begin();
+ it != mUserSetColors.end();
+ ++it)
+ {
+ ColorEntryParams color_entry;
+ color_entry.name = it->first;
+ color_entry.color.value = it->second;
+
+ params.color_entries.add(color_entry);
+ }
+
+ LLXMLNodePtr output_node = new LLXMLNode("colors", false);
+ LLXUIParser::instance().writeXUI(output_node, params);
+
+ if(!output_node->isNull())
+ {
+ const std::string& filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "colors.xml");
+ LLFILE *fp = LLFile::fopen(filename, "w");
+
+ if(fp != NULL)
+ {
+ LLXMLNode::writeHeaderToFile(fp);
+ output_node->writeToFile(fp);
+
+ fclose(fp);
}
}
}
-const LLColor4& LLUIColorTable::getColor(const std::string& name) const
+bool LLUIColorTable::colorExists(const std::string& color_name) const
+{
+ return ((mLoadedColors.find(color_name) != mLoadedColors.end())
+ || (mUserSetColors.find(color_name) != mUserSetColors.end()));
+}
+
+void LLUIColorTable::clearTable(string_color_map_t& table)
{
- string_color_map_t::const_iterator iter = mColors.find(name);
- return (iter != mColors.end() ? iter->second : LLColor4::magenta);
+ for(string_color_map_t::iterator it = table.begin();
+ it != table.end();
+ ++it)
+ {
+ it->second = LLColor4::magenta;
+ }
}
+
+// this method inserts a color into the table if it does not exist
+// if the color already exists it changes the color
+void LLUIColorTable::setColor(const std::string& name, const LLColor4& color, string_color_map_t& table)
+{
+ string_color_map_t::iterator it = table.lower_bound(name);
+ if(it != table.end()
+ && !(table.key_comp()(name, it->first)))
+ {
+ it->second = color;
+ }
+ else
+ {
+ table.insert(it, string_color_map_t::value_type(name, color));
+ }
+}
+
+bool LLUIColorTable::loadFromFilename(const std::string& filename, string_color_map_t& table)
+{
+ LLXMLNodePtr root;
+
+ if(!LLXMLNode::parseFile(filename, root, NULL))
+ {
+ llwarns << "Unable to parse color file " << filename << llendl;
+ return false;
+ }
+
+ if(!root->hasName("colors"))
+ {
+ llwarns << filename << " is not a valid color definition file" << llendl;
+ return false;
+ }
+
+ Params params;
+ LLXUIParser::instance().readXUI(root, params, filename);
+
+ if(params.validateBlock())
+ {
+ insertFromParams(params, table);
+ }
+ else
+ {
+ llwarns << filename << " failed to load" << llendl;
+ return false;
+ }
+
+ 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 8900875813..76518789ec 100644
--- a/indra/llui/lluicolortable.h
+++ b/indra/llui/lluicolortable.h
@@ -2,8 +2,25 @@
* @file lluicolortable.h
* @brief brief LLUIColorTable class header file
*
- * $LicenseInfo:firstyear=2009&license=viewergpl$
- * Copyright (c) 2009, Linden Research, Inc.
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -17,13 +34,20 @@
#include "v4color.h"
+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>
{
- Option<LLColor4> value;
- Option<std::string> reference;
+ Alternative<LLColor4> value;
+ Alternative<std::string> reference;
ColorParams();
};
@@ -44,15 +68,36 @@ public:
};
// define colors by passing in a param block that can be generated via XUI file or manually
- void init(const Params& p);
+ void insertFromParams(const Params& p);
+
+ // reset all colors to default magenta color
+ void clear();
// color lookup
- const LLColor4& getColor(const std::string& name) const;
+ LLUIColor getColor(const std::string& name, const LLColor4& default_color = LLColor4::magenta) const;
+
+ // if the color is in the table, it's value is changed, otherwise it is added
+ void setColor(const std::string& name, const LLColor4& color);
+
+ // returns true if color_name exists in the table
+ bool colorExists(const std::string& color_name) const;
+
+ // loads colors from settings files
+ bool loadFromSettings();
+
+ // saves colors specified by the user to the users skin directory
+ void saveUserSettings() const;
private:
- // consider using sorted vector
- typedef std::map<std::string, LLColor4> string_color_map_t;
- string_color_map_t mColors;
+ bool loadFromFilename(const std::string& filename, string_color_map_t& table);
+
+ 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);
+
+ string_color_map_t mLoadedColors;
+ string_color_map_t mUserSetColors;
};
#endif // LL_LLUICOLORTABLE_H
diff --git a/indra/llui/lluiconstants.h b/indra/llui/lluiconstants.h
index f04062a0ed..1479e58c43 100644
--- a/indra/llui/lluiconstants.h
+++ b/indra/llui/lluiconstants.h
@@ -2,31 +2,25 @@
* @file lluiconstants.h
* @brief Compile-time configuration for UI
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llui/lluictrl.cpp b/indra/llui/lluictrl.cpp
index 80ef7ebdf1..3ac3bf8c41 100644
--- a/indra/llui/lluictrl.cpp
+++ b/indra/llui/lluictrl.cpp
@@ -3,122 +3,114 @@
* @author James Cook, Richard Nelson, Tom Yedwab
* @brief Abstract base class for UI controls
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
-//#include "llviewerprecompiledheaders.h"
#include "linden_common.h"
+
+#define LLUICTRL_CPP
#include "lluictrl.h"
+
#include "llfocusmgr.h"
#include "llpanel.h"
#include "lluictrlfactory.h"
-static LLDefaultWidgetRegistry::Register<LLUICtrl> r("ui_ctrl");
+static LLDefaultChildRegistry::Register<LLUICtrl> r("ui_ctrl");
-LLUICtrl::Params::Params()
-: tab_stop("tab_stop", true),
- label("label"),
- initial_value("initial_value"),
- init_callback("init_callback"),
- commit_callback("commit_callback"),
- validate_callback("validate_callback"),
- rightclick_callback("rightclick_callback"),
- control_name("control_name")
-{
- addSynonym(initial_value, "initial_val");
- // this is the canonical name for text contents of an xml node
- addSynonym(initial_value, "value");
-}
+// Compiler optimization, generate extern template
+template class LLUICtrl* LLView::getChild<class LLUICtrl>(
+ const std::string& name, BOOL recurse) const;
-LLFocusableElement::LLFocusableElement()
-: mFocusLostCallback(NULL),
- mFocusReceivedCallback(NULL),
- mFocusChangedCallback(NULL),
- mTopLostCallback(NULL),
- mFocusCallbackUserData(NULL)
+LLUICtrl::CallbackParam::CallbackParam()
+: name("name"),
+ function_name("function"),
+ parameter("parameter"),
+ control_name("control") // Shortcut to control -> "control_name" for backwards compatability
{
+ addSynonym(parameter, "userdata");
}
-//virtual
-LLFocusableElement::~LLFocusableElement()
-{
-}
+LLUICtrl::EnableControls::EnableControls()
+: enabled("enabled_control"),
+ disabled("disabled_control")
+{}
-void LLFocusableElement::onFocusReceived()
+LLUICtrl::ControlVisibility::ControlVisibility()
+: visible("visibility_control"),
+ invisible("invisibility_control")
{
- if( mFocusReceivedCallback )
- {
- mFocusReceivedCallback( this, mFocusCallbackUserData );
- }
- if( mFocusChangedCallback )
- {
- mFocusChangedCallback( this, mFocusCallbackUserData );
- }
+ addSynonym(visible, "visiblity_control");
+ addSynonym(invisible, "invisiblity_control");
}
-void LLFocusableElement::onFocusLost()
+LLUICtrl::Params::Params()
+: tab_stop("tab_stop", true),
+ chrome("chrome", false),
+ label("label"),
+ initial_value("value"),
+ init_callback("init_callback"),
+ commit_callback("commit_callback"),
+ validate_callback("validate_callback"),
+ mouseenter_callback("mouseenter_callback"),
+ mouseleave_callback("mouseleave_callback"),
+ control_name("control_name"),
+ font("font", LLFontGL::getFontSansSerif()),
+ font_halign("halign"),
+ font_valign("valign"),
+ length("length"), // ignore LLXMLNode cruft
+ type("type") // ignore LLXMLNode cruft
{
- if( mFocusLostCallback )
- {
- mFocusLostCallback( this, mFocusCallbackUserData );
- }
-
- if( mFocusChangedCallback )
- {
- mFocusChangedCallback( this, mFocusCallbackUserData );
- }
+ addSynonym(initial_value, "initial_value");
}
-void LLFocusableElement::onTopLost()
-{
- if (mTopLostCallback)
- {
- mTopLostCallback(this, mFocusCallbackUserData);
- }
-}
+// NOTE: the LLFocusableElement implementation has been moved from here to llfocusmgr.cpp.
-BOOL LLFocusableElement::hasFocus() const
+//static
+const LLUICtrl::Params& LLUICtrl::getDefaultParams()
{
- return FALSE;
+ return LLUICtrlFactory::getDefaultParams<LLUICtrl>();
}
-void LLFocusableElement::setFocus(BOOL b)
-{
-}
LLUICtrl::LLUICtrl(const LLUICtrl::Params& p, const LLViewModelPtr& viewmodel)
: LLView(p),
mTentative(FALSE),
mIsChrome(FALSE),
+ mTabStop(FALSE),
mViewModel(viewmodel),
mControlVariable(NULL),
mEnabledControlVariable(NULL),
- mDisabledControlVariable(NULL)
+ mDisabledControlVariable(NULL),
+ mMakeVisibleControlVariable(NULL),
+ mMakeInvisibleControlVariable(NULL),
+ mCommitSignal(NULL),
+ mValidateSignal(NULL),
+ mMouseEnterSignal(NULL),
+ mMouseLeaveSignal(NULL),
+ mMouseDownSignal(NULL),
+ mMouseUpSignal(NULL),
+ mRightMouseDownSignal(NULL),
+ mRightMouseUpSignal(NULL),
+ mDoubleClickSignal(NULL)
{
mUICtrlHandle.bind(this);
}
@@ -127,6 +119,7 @@ void LLUICtrl::initFromParams(const Params& p)
{
LLView::initFromParams(p);
+ setIsChrome(p.chrome);
setControlName(p.control_name);
if(p.enabled_controls.isProvided())
{
@@ -160,7 +153,6 @@ void LLUICtrl::initFromParams(const Params& p)
}
setTabStop(p.tab_stop);
- setFocusLostCallback(p.focus_lost_callback());
if (p.initial_value.isProvided()
&& !p.control_name.isProvided())
@@ -169,10 +161,14 @@ void LLUICtrl::initFromParams(const Params& p)
}
if (p.commit_callback.isProvided())
- initCommitCallback(p.commit_callback, mCommitSignal);
+ {
+ setCommitCallback(initCommitCallback(p.commit_callback));
+ }
if (p.validate_callback.isProvided())
- initEnableCallback(p.validate_callback, mValidateSignal);
+ {
+ setValidateCallback(initEnableCallback(p.validate_callback));
+ }
if (p.init_callback.isProvided())
{
@@ -182,7 +178,7 @@ void LLUICtrl::initFromParams(const Params& p)
}
else
{
- commit_callback_t* initfunc = (CallbackRegistry<commit_callback_t>::getValue(p.init_callback.function_name));
+ commit_callback_t* initfunc = (CommitCallbackRegistry::getValue(p.init_callback.function_name));
if (initfunc)
{
(*initfunc)(this, p.init_callback.parameter);
@@ -190,9 +186,15 @@ void LLUICtrl::initFromParams(const Params& p)
}
}
- if(p.rightclick_callback.isProvided())
- initCommitCallback(p.rightclick_callback, mRightClickSignal);
+ if(p.mouseenter_callback.isProvided())
+ {
+ setMouseEnterCallback(initCommitCallback(p.mouseenter_callback));
+ }
+ if(p.mouseleave_callback.isProvided())
+ {
+ setMouseLeaveCallback(initCommitCallback(p.mouseleave_callback));
+ }
}
@@ -205,44 +207,64 @@ LLUICtrl::~LLUICtrl()
llwarns << "UI Control holding top ctrl deleted: " << getName() << ". Top view removed." << llendl;
gFocusMgr.removeTopCtrlWithoutCallback( this );
}
+
+ delete mCommitSignal;
+ delete mValidateSignal;
+ delete mMouseEnterSignal;
+ delete mMouseLeaveSignal;
+ delete mMouseDownSignal;
+ delete mMouseUpSignal;
+ delete mRightMouseDownSignal;
+ delete mRightMouseUpSignal;
+ delete mDoubleClickSignal;
+}
+
+void default_commit_handler(LLUICtrl* ctrl, const LLSD& param)
+{}
+
+bool default_enable_handler(LLUICtrl* ctrl, const LLSD& param)
+{
+ return true;
}
-void LLUICtrl::initCommitCallback(const CommitCallbackParam& cb, commit_signal_t& sig)
+
+LLUICtrl::commit_signal_t::slot_type LLUICtrl::initCommitCallback(const CommitCallbackParam& cb)
{
if (cb.function.isProvided())
{
if (cb.parameter.isProvided())
- sig.connect(boost::bind(cb.function(), _1, cb.parameter));
+ return boost::bind(cb.function(), _1, cb.parameter);
else
- sig.connect(cb.function());
+ return cb.function();
}
else
{
std::string function_name = cb.function_name;
- commit_callback_t* func = (CallbackRegistry<commit_callback_t>::getValue(function_name));
+ commit_callback_t* func = (CommitCallbackRegistry::getValue(function_name));
if (func)
{
if (cb.parameter.isProvided())
- sig.connect(boost::bind((*func), _1, cb.parameter));
+ return boost::bind((*func), _1, cb.parameter);
else
- sig.connect(*func);
+ return commit_signal_t::slot_type(*func);
}
else if (!function_name.empty())
{
llwarns << "No callback found for: '" << function_name << "' in control: " << getName() << llendl;
}
}
+ return default_commit_handler;
}
-void LLUICtrl::initEnableCallback(const EnableCallbackParam& cb, enable_signal_t& sig)
+LLUICtrl::enable_signal_t::slot_type LLUICtrl::initEnableCallback(const EnableCallbackParam& cb)
{
// Set the callback function
if (cb.function.isProvided())
{
if (cb.parameter.isProvided())
- sig.connect(boost::bind(cb.function(), this, cb.parameter));
+ return boost::bind(cb.function(), this, cb.parameter);
else
- sig.connect(cb.function());
+ return cb.function();
}
else
{
@@ -250,17 +272,97 @@ void LLUICtrl::initEnableCallback(const EnableCallbackParam& cb, enable_signal_t
if (func)
{
if (cb.parameter.isProvided())
- sig.connect(boost::bind((*func), this, cb.parameter));
+ return boost::bind((*func), this, cb.parameter);
else
- sig.connect(*func);
+ return enable_signal_t::slot_type(*func);
}
}
+ return default_enable_handler;
+}
+
+// virtual
+void LLUICtrl::onMouseEnter(S32 x, S32 y, MASK mask)
+{
+ if (mMouseEnterSignal)
+ {
+ (*mMouseEnterSignal)(this, getValue());
+ }
+}
+
+// virtual
+void LLUICtrl::onMouseLeave(S32 x, S32 y, MASK mask)
+{
+ if(mMouseLeaveSignal)
+ {
+ (*mMouseLeaveSignal)(this, getValue());
+ }
+}
+
+//virtual
+BOOL LLUICtrl::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = LLView::handleMouseDown(x,y,mask);
+ if (mMouseDownSignal)
+ {
+ (*mMouseDownSignal)(this,x,y,mask);
+ }
+ return handled;
+}
+
+//virtual
+BOOL LLUICtrl::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = LLView::handleMouseUp(x,y,mask);
+ if (mMouseUpSignal)
+ {
+ (*mMouseUpSignal)(this,x,y,mask);
+ }
+ return handled;
+}
+
+//virtual
+BOOL LLUICtrl::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = LLView::handleRightMouseDown(x,y,mask);
+ if (mRightMouseDownSignal)
+ {
+ (*mRightMouseDownSignal)(this,x,y,mask);
+ }
+ return handled;
+}
+
+//virtual
+BOOL LLUICtrl::handleRightMouseUp(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = LLView::handleRightMouseUp(x,y,mask);
+ if(mRightMouseUpSignal)
+ {
+ (*mRightMouseUpSignal)(this,x,y,mask);
+ }
+ return handled;
+}
+
+BOOL LLUICtrl::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = LLView::handleDoubleClick(x, y, mask);
+ if (mDoubleClickSignal)
+ {
+ (*mDoubleClickSignal)(this, x, y, mask);
+ }
+ return handled;
+}
+
+// can't tab to children of a non-tab-stop widget
+BOOL LLUICtrl::canFocusChildren() const
+{
+ return hasTabStop();
}
void LLUICtrl::onCommit()
{
- mCommitSignal(this, getValue());
+ if (mCommitSignal)
+ (*mCommitSignal)(this, getValue());
}
//virtual
@@ -493,56 +595,6 @@ void LLUICtrl::setFocus(BOOL b)
}
}
-void LLUICtrl::onFocusReceived()
-{
- // trigger callbacks
- LLFocusableElement::onFocusReceived();
-
- // find first view in hierarchy above new focus that is a LLUICtrl
- LLView* viewp = getParent();
- LLUICtrl* last_focus = gFocusMgr.getLastKeyboardFocus();
-
- while (viewp && !viewp->isCtrl())
- {
- viewp = viewp->getParent();
- }
-
- // and if it has newly gained focus, call onFocusReceived()
- LLUICtrl* ctrlp = static_cast<LLUICtrl*>(viewp);
- if (ctrlp && (!last_focus || !last_focus->hasAncestor(ctrlp)))
- {
- ctrlp->onFocusReceived();
- }
-}
-
-void LLUICtrl::onFocusLost()
-{
- // trigger callbacks
- LLFocusableElement::onFocusLost();
-
- // find first view in hierarchy above old focus that is a LLUICtrl
- LLView* viewp = getParent();
- while (viewp && !viewp->isCtrl())
- {
- viewp = viewp->getParent();
- }
-
- // and if it has just lost focus, call onFocusReceived()
- LLUICtrl* ctrlp = static_cast<LLUICtrl*>(viewp);
- // hasFocus() includes any descendants
- if (ctrlp && !ctrlp->hasFocus())
- {
- ctrlp->onFocusLost();
- }
-}
-
-void LLUICtrl::onTopLost()
-{
- // trigger callbacks
- LLFocusableElement::onTopLost();
-}
-
-
// virtual
void LLUICtrl::setTabStop( BOOL b )
{
@@ -592,7 +644,6 @@ void LLUICtrl::setIsChrome(BOOL is_chrome)
// virtual
BOOL LLUICtrl::getIsChrome() const
{
-
LLView* parent_ctrl = getParent();
while(parent_ctrl)
{
@@ -619,7 +670,7 @@ BOOL LLUICtrl::getIsChrome() const
class CompareByDefaultTabGroup: public LLCompareByTabOrder
{
public:
- CompareByDefaultTabGroup(LLView::child_tab_order_t order, S32 default_tab_group):
+ CompareByDefaultTabGroup(const LLView::child_tab_order_t& order, S32 default_tab_group):
LLCompareByTabOrder(order),
mDefaultTabGroup(default_tab_group) {}
private:
@@ -643,13 +694,16 @@ class LLUICtrl::DefaultTabGroupFirstSorter : public LLQuerySorter, public LLSing
{
public:
/*virtual*/ void operator() (LLView * parent, viewList_t &children) const
- {
+ {
children.sort(CompareByDefaultTabGroup(parent->getCtrlOrder(), parent->getDefaultTabGroup()));
}
};
+LLFastTimer::DeclareTimer FTM_FOCUS_FIRST_ITEM("Focus First Item");
+
BOOL LLUICtrl::focusFirstItem(BOOL prefer_text_fields, BOOL focus_flash)
{
+ LLFastTimer _(FTM_FOCUS_FIRST_ITEM);
// try to select default tab group child
LLCtrlQuery query = getTabOrderQuery();
// sort things such that the default tab group is at the front
@@ -781,38 +835,6 @@ LLUICtrl* LLUICtrl::findRootMostFocusRoot()
return focus_root;
}
-
-/*
-// Don't let the children handle the tool tip. Handle it here instead.
-BOOL LLUICtrl::handleToolTip(S32 x, S32 y, std::string& msg, LLRect* sticky_rect_screen)
-{
- BOOL handled = FALSE;
- if (getVisible() && pointInView( x, y ) )
- {
- if( !mToolTipMsg.empty() )
- {
- msg = mToolTipMsg;
-
- // Convert rect local to screen coordinates
- localPointToScreen(
- 0, 0,
- &(sticky_rect_screen->mLeft), &(sticky_rect_screen->mBottom) );
- localPointToScreen(
- getRect().getWidth(), getRect().getHeight(),
- &(sticky_rect_screen->mRight), &(sticky_rect_screen->mTop) );
-
- handled = TRUE;
- }
- }
-
- if (!handled)
- {
- return LLView::handleToolTip(x, y, msg, sticky_rect_screen);
- }
-
- return handled;
-}*/
-
// Skip over any parents that are not LLUICtrl's
// Used in focus logic since only LLUICtrl elements can have focus
LLUICtrl* LLUICtrl::getParentUICtrl() const
@@ -832,14 +854,57 @@ LLUICtrl* LLUICtrl::getParentUICtrl() const
return NULL;
}
+bool LLUICtrl::findHelpTopic(std::string& help_topic_out)
+{
+ LLUICtrl* ctrl = this;
+
+ // search back through the control's parents for a panel
+ // or tab with a help_topic string defined
+ while (ctrl)
+ {
+ LLPanel *panel = dynamic_cast<LLPanel *>(ctrl);
+
+ if (panel)
+ {
+ // does the panel have a sub-panel with a help topic?
+ LLPanel *subpanel = panel->childGetVisiblePanelWithHelp();
+ if (subpanel)
+ {
+ help_topic_out = subpanel->getHelpTopic();
+ return true; // success (subpanel)
+ }
+
+ // does the panel have an active tab with a help topic?
+ LLPanel *tab = panel->childGetVisibleTabWithHelp();
+ if (tab)
+ {
+ help_topic_out = tab->getHelpTopic();
+ return true; // success (tab)
+ }
+
+ // otherwise, does the panel have a help topic itself?
+ if (!panel->getHelpTopic().empty())
+ {
+ help_topic_out = panel->getHelpTopic();
+ return true; // success (panel)
+ }
+ }
+
+ ctrl = ctrl->getParentUICtrl();
+ }
+
+ return false; // no help topic found
+}
+
// *TODO: Deprecate; for backwards compatability only:
-boost::signals::connection LLUICtrl::setCommitCallback( boost::function<void (LLUICtrl*,void*)> cb, void* data)
+boost::signals2::connection LLUICtrl::setCommitCallback( boost::function<void (LLUICtrl*,void*)> cb, void* data)
{
return setCommitCallback( boost::bind(cb, _1, data));
}
-boost::signals::connection LLUICtrl::setValidateBeforeCommit( boost::function<bool (const LLSD& data)> cb )
+boost::signals2::connection LLUICtrl::setValidateBeforeCommit( boost::function<bool (const LLSD& data)> cb )
{
- return mValidateSignal.connect(boost::bind(cb, _2));
+ if (!mValidateSignal) mValidateSignal = new enable_signal_t();
+ return mValidateSignal->connect(boost::bind(cb, _2));
}
// virtual
@@ -858,47 +923,56 @@ BOOL LLUICtrl::getTentative() const
void LLUICtrl::setColor(const LLColor4& color)
{ }
-// virtual
-void LLUICtrl::setMinValue(LLSD min_value)
-{ }
+boost::signals2::connection LLUICtrl::setCommitCallback( const commit_signal_t::slot_type& cb )
+{
+ if (!mCommitSignal) mCommitSignal = new commit_signal_t();
+ return mCommitSignal->connect(cb);
+}
-// virtual
-void LLUICtrl::setMaxValue(LLSD max_value)
-{ }
+boost::signals2::connection LLUICtrl::setValidateCallback( const enable_signal_t::slot_type& cb )
+{
+ if (!mValidateSignal) mValidateSignal = new enable_signal_t();
+ return mValidateSignal->connect(cb);
+}
+boost::signals2::connection LLUICtrl::setMouseEnterCallback( const commit_signal_t::slot_type& cb )
+{
+ if (!mMouseEnterSignal) mMouseEnterSignal = new commit_signal_t();
+ return mMouseEnterSignal->connect(cb);
+}
+boost::signals2::connection LLUICtrl::setMouseLeaveCallback( const commit_signal_t::slot_type& cb )
+{
+ if (!mMouseLeaveSignal) mMouseLeaveSignal = new commit_signal_t();
+ return mMouseLeaveSignal->connect(cb);
+}
-namespace LLInitParam
-{
- template<>
- bool ParamCompare<LLUICtrl::commit_callback_t>::equals(
- const LLUICtrl::commit_callback_t &a,
- const LLUICtrl::commit_callback_t &b)
- {
- return false;
- }
-
- template<>
- bool ParamCompare<LLUICtrl::focus_callback_t>::equals(
- const LLUICtrl::focus_callback_t &a,
- const LLUICtrl::focus_callback_t &b)
- {
- return false;
- }
-
- template<>
- bool ParamCompare<LLUICtrl::enable_callback_t>::equals(
- const LLUICtrl::enable_callback_t &a,
- const LLUICtrl::enable_callback_t &b)
- {
- return false;
- }
-
- template<>
- bool ParamCompare<LLLazyValue<LLColor4> >::equals(
- const LLLazyValue<LLColor4> &a,
- const LLLazyValue<LLColor4> &b)
- {
- return a.get() == b.get();
- }
+boost::signals2::connection LLUICtrl::setMouseDownCallback( const mouse_signal_t::slot_type& cb )
+{
+ if (!mMouseDownSignal) mMouseDownSignal = new mouse_signal_t();
+ return mMouseDownSignal->connect(cb);
+}
+
+boost::signals2::connection LLUICtrl::setMouseUpCallback( const mouse_signal_t::slot_type& cb )
+{
+ if (!mMouseUpSignal) mMouseUpSignal = new mouse_signal_t();
+ return mMouseUpSignal->connect(cb);
+}
+
+boost::signals2::connection LLUICtrl::setRightMouseDownCallback( const mouse_signal_t::slot_type& cb )
+{
+ if (!mRightMouseDownSignal) mRightMouseDownSignal = new mouse_signal_t();
+ return mRightMouseDownSignal->connect(cb);
+}
+
+boost::signals2::connection LLUICtrl::setRightMouseUpCallback( const mouse_signal_t::slot_type& cb )
+{
+ if (!mRightMouseUpSignal) mRightMouseUpSignal = new mouse_signal_t();
+ return mRightMouseUpSignal->connect(cb);
+}
+
+boost::signals2::connection LLUICtrl::setDoubleClickCallback( const mouse_signal_t::slot_type& cb )
+{
+ if (!mDoubleClickSignal) mDoubleClickSignal = new mouse_signal_t();
+ return mDoubleClickSignal->connect(cb);
}
diff --git a/indra/llui/lluictrl.h b/indra/llui/lluictrl.h
index 71f0a47f45..76dfdf754c 100644
--- a/indra/llui/lluictrl.h
+++ b/indra/llui/lluictrl.h
@@ -3,155 +3,120 @@
* @author James Cook, Richard Nelson, Tom Yedwab
* @brief Abstract base class for UI controls
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LL_LLUICTRL_H
#define LL_LLUICTRL_H
-#include "llboost.h"
+//#include "llboost.h"
#include "llrect.h"
#include "llsd.h"
#include <boost/function.hpp>
+#include <boost/signals2.hpp>
#include "llinitparam.h"
#include "llview.h"
-#include "llviewmodel.h"
+#include "llviewmodel.h" // *TODO move dependency to .cpp file
const BOOL TAKE_FOCUS_YES = TRUE;
const BOOL TAKE_FOCUS_NO = FALSE;
-class LLFocusableElement
-{
- friend class LLFocusMgr; // allow access to focus change handlers
-public:
- LLFocusableElement();
- virtual ~LLFocusableElement();
-
- virtual void setFocus( BOOL b );
- virtual BOOL hasFocus() const;
-
- typedef boost::function<void(LLFocusableElement*, void*)> focus_callback_t;
- void setFocusLostCallback(focus_callback_t cb, void* user_data = NULL) { mFocusLostCallback = cb; mFocusCallbackUserData = user_data; }
- void setFocusReceivedCallback(focus_callback_t cb, void* user_data = NULL) { mFocusReceivedCallback = cb; mFocusCallbackUserData = user_data; }
- void setFocusChangedCallback(focus_callback_t cb, void* user_data = NULL ) { mFocusChangedCallback = cb; mFocusCallbackUserData = user_data; }
- void setTopLostCallback(focus_callback_t cb, void* user_data = NULL ) { mTopLostCallback = cb; mFocusCallbackUserData = user_data; }
-
-protected:
- virtual void onFocusReceived();
- virtual void onFocusLost();
- virtual void onTopLost(); // called when registered as top ctrl and user clicks elsewhere
- focus_callback_t mFocusLostCallback;
- focus_callback_t mFocusReceivedCallback;
- focus_callback_t mFocusChangedCallback;
- focus_callback_t mTopLostCallback;
- void* mFocusCallbackUserData;
-};
-
class LLUICtrl
- : public LLView, public LLFocusableElement, public boost::signals::trackable
+ : public LLView, public boost::signals2::trackable
{
public:
-
-
typedef boost::function<void (LLUICtrl* ctrl, const LLSD& param)> commit_callback_t;
- typedef boost::signal<void (LLUICtrl* ctrl, const LLSD& param)> commit_signal_t;
+ typedef boost::signals2::signal<void (LLUICtrl* ctrl, const LLSD& param)> commit_signal_t;
+ // *TODO: add xml support for this type of signal in the future
+ typedef boost::signals2::signal<void (LLUICtrl* ctrl, S32 x, S32 y, MASK mask)> mouse_signal_t;
typedef boost::function<bool (LLUICtrl* ctrl, const LLSD& param)> enable_callback_t;
- typedef boost::signal<bool (LLUICtrl* ctrl, const LLSD& param), boost_boolean_combiner> enable_signal_t;
+ typedef boost::signals2::signal<bool (LLUICtrl* ctrl, const LLSD& param), boost_boolean_combiner> enable_signal_t;
struct CallbackParam : public LLInitParam::Block<CallbackParam>
{
- Deprecated name;
+ Ignored name;
Optional<std::string> function_name;
Optional<LLSD> parameter;
Optional<std::string> control_name;
- CallbackParam()
- : name("name"),
- function_name("function"),
- parameter("parameter"),
- control_name("control") // Shortcut to control -> "control_name" for backwards compatability
- {
- addSynonym(parameter, "userdata");
- }
+ CallbackParam();
};
struct CommitCallbackParam : public LLInitParam::Block<CommitCallbackParam, CallbackParam >
{
Optional<commit_callback_t> function;
};
-
+
+ // also used for visible callbacks
struct EnableCallbackParam : public LLInitParam::Block<EnableCallbackParam, CallbackParam >
{
Optional<enable_callback_t> function;
};
-
+
struct EnableControls : public LLInitParam::Choice<EnableControls>
{
- Option<std::string> enabled;
- Option<std::string> disabled;
+ Alternative<std::string> enabled;
+ Alternative<std::string> disabled;
- EnableControls()
- : enabled("enabled_control"),
- disabled("disabled_control")
- {}
+ EnableControls();
};
struct ControlVisibility : public LLInitParam::Choice<ControlVisibility>
{
- Option<std::string> visible;
- Option<std::string> invisible;
+ Alternative<std::string> visible;
+ Alternative<std::string> invisible;
- ControlVisibility()
- : visible("make_visible_control"),
- invisible("make_invisible_control")
- {}
+ ControlVisibility();
};
struct Params : public LLInitParam::Block<Params, LLView::Params>
{
Optional<std::string> label;
- Optional<bool> tab_stop;
+ Optional<bool> tab_stop,
+ chrome;
Optional<LLSD> initial_value;
Optional<CommitCallbackParam> init_callback,
commit_callback;
Optional<EnableCallbackParam> validate_callback;
- Optional<CommitCallbackParam> rightclick_callback;
-
- Optional<focus_callback_t> focus_lost_callback;
+ Optional<CommitCallbackParam> mouseenter_callback,
+ mouseleave_callback;
Optional<std::string> control_name;
Optional<EnableControls> enabled_controls;
Optional<ControlVisibility> controls_visibility;
+ // font params
+ Optional<const LLFontGL*> font;
+ Optional<LLFontGL::HAlign> font_halign;
+ Optional<LLFontGL::VAlign> font_valign;
+
+ // cruft from LLXMLNode implementation
+ Ignored type,
+ length;
+
Params();
};
@@ -160,11 +125,12 @@ public:
void initFromParams(const Params& p);
protected:
friend class LLUICtrlFactory;
- LLUICtrl(const Params& p = LLUICtrl::Params(),
+ static const Params& getDefaultParams();
+ LLUICtrl(const Params& p = getDefaultParams(),
const LLViewModelPtr& viewmodel=LLViewModelPtr(new LLViewModel));
- void initCommitCallback(const CommitCallbackParam& cb, commit_signal_t& sig);
- void initEnableCallback(const EnableCallbackParam& cb, enable_signal_t& sig);
+ commit_signal_t::slot_type initCommitCallback(const CommitCallbackParam& cb);
+ enable_signal_t::slot_type initEnableCallback(const EnableCallbackParam& cb);
// We need this virtual so we can override it with derived versions
virtual LLViewModel* getViewModel() const;
@@ -174,12 +140,15 @@ protected:
public:
// LLView interface
/*virtual*/ BOOL setLabelArg( const std::string& key, const LLStringExplicit& text );
- /*virtual*/ void onFocusReceived();
- /*virtual*/ void onFocusLost();
- /*virtual*/ void onTopLost();
/*virtual*/ BOOL isCtrl() const;
- /*virtual*/ void setTentative(BOOL b);
- /*virtual*/ BOOL getTentative() const;
+ /*virtual*/ void onMouseEnter(S32 x, S32 y, MASK mask);
+ /*virtual*/ void onMouseLeave(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL canFocusChildren() const;
+ /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleRightMouseUp(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleDoubleClick(S32 x, S32 y, MASK mask);
// From LLFocusableElement
/*virtual*/ void setFocus( BOOL b );
@@ -203,6 +172,8 @@ public:
void setMakeVisibleControlVariable(LLControlVariable* control);
void setMakeInvisibleControlVariable(LLControlVariable* control);
+ virtual void setTentative(BOOL b);
+ virtual BOOL getTentative() const;
virtual void setValue(const LLSD& value);
virtual LLSD getValue() const;
/// When two widgets are displaying the same data (e.g. during a skin
@@ -224,10 +195,12 @@ public:
// Default to no-op:
virtual void onTabInto();
+
+ // Clear any user-provided input (text in a text editor, checked checkbox,
+ // selected radio button, etc.). Defaults to no-op.
virtual void clear();
+
virtual void setColor(const LLColor4& color);
- virtual void setMinValue(LLSD min_value);
- virtual void setMaxValue(LLSD max_value);
BOOL focusNextItem(BOOL text_entry_only);
BOOL focusPrevItem(BOOL text_entry_only);
@@ -243,13 +216,27 @@ public:
LLUICtrl* getParentUICtrl() const;
- boost::signals::connection setCommitCallback( const commit_signal_t::slot_type& cb ) { return mCommitSignal.connect(cb); }
- boost::signals::connection setValidateCallback( const enable_signal_t::slot_type& cb ) { return mValidateSignal.connect(cb); }
+ // return true if help topic found by crawling through parents -
+ // topic then put in help_topic_out
+ bool findHelpTopic(std::string& help_topic_out);
+
+ boost::signals2::connection setCommitCallback( const commit_signal_t::slot_type& cb );
+ boost::signals2::connection setValidateCallback( const enable_signal_t::slot_type& cb );
+
+ boost::signals2::connection setMouseEnterCallback( const commit_signal_t::slot_type& cb );
+ boost::signals2::connection setMouseLeaveCallback( const commit_signal_t::slot_type& cb );
- // *TODO: Deprecate; for backwards compatability only:
- boost::signals::connection setCommitCallback( boost::function<void (LLUICtrl*,void*)> cb, void* data);
- boost::signals::connection setValidateBeforeCommit( boost::function<bool (const LLSD& data)> cb );
+ boost::signals2::connection setMouseDownCallback( const mouse_signal_t::slot_type& cb );
+ boost::signals2::connection setMouseUpCallback( const mouse_signal_t::slot_type& cb );
+ boost::signals2::connection setRightMouseDownCallback( const mouse_signal_t::slot_type& cb );
+ boost::signals2::connection setRightMouseUpCallback( const mouse_signal_t::slot_type& cb );
+ boost::signals2::connection setDoubleClickCallback( const mouse_signal_t::slot_type& cb );
+
+ // *TODO: Deprecate; for backwards compatability only:
+ boost::signals2::connection setCommitCallback( boost::function<void (LLUICtrl*,void*)> cb, void* data);
+ boost::signals2::connection setValidateBeforeCommit( boost::function<bool (const LLSD& data)> cb );
+
LLUICtrl* findRootMostFocusRoot();
class LLTextInputFilter : public LLQueryFilter, public LLSingleton<LLTextInputFilter>
@@ -260,32 +247,42 @@ public:
}
};
- template <typename F> class CallbackRegistry : public LLRegistrySingleton<std::string, F, CallbackRegistry<F> >
+ template <typename F, typename DERIVED> class CallbackRegistry : public LLRegistrySingleton<std::string, F, DERIVED >
{};
- typedef CallbackRegistry<commit_callback_t> CommitCallbackRegistry;
- typedef CallbackRegistry<enable_callback_t> EnableCallbackRegistry;
-
+ class CommitCallbackRegistry : public CallbackRegistry<commit_callback_t, CommitCallbackRegistry>{};
+ // the enable callback registry is also used for visiblity callbacks
+ class EnableCallbackRegistry : public CallbackRegistry<enable_callback_t, EnableCallbackRegistry>{};
+
protected:
static bool controlListener(const LLSD& newvalue, LLHandle<LLUICtrl> handle, std::string type);
- commit_signal_t mCommitSignal;
- enable_signal_t mValidateSignal;
- commit_signal_t mRightClickSignal;
+ commit_signal_t* mCommitSignal;
+ enable_signal_t* mValidateSignal;
+ commit_signal_t* mMouseEnterSignal;
+ commit_signal_t* mMouseLeaveSignal;
+
+ mouse_signal_t* mMouseDownSignal;
+ mouse_signal_t* mMouseUpSignal;
+ mouse_signal_t* mRightMouseDownSignal;
+ mouse_signal_t* mRightMouseUpSignal;
+
+ mouse_signal_t* mDoubleClickSignal;
+
LLViewModelPtr mViewModel;
LLControlVariable* mControlVariable;
- boost::signals::connection mControlConnection;
+ boost::signals2::connection mControlConnection;
LLControlVariable* mEnabledControlVariable;
- boost::signals::connection mEnabledControlConnection;
+ boost::signals2::connection mEnabledControlConnection;
LLControlVariable* mDisabledControlVariable;
- boost::signals::connection mDisabledControlConnection;
+ boost::signals2::connection mDisabledControlConnection;
LLControlVariable* mMakeVisibleControlVariable;
- boost::signals::connection mMakeVisibleControlConnection;
+ boost::signals2::connection mMakeVisibleControlConnection;
LLControlVariable* mMakeInvisibleControlVariable;
- boost::signals::connection mMakeInvisibleControlConnection;
+ boost::signals2::connection mMakeInvisibleControlConnection;
private:
BOOL mTabStop;
@@ -296,22 +293,10 @@ private:
class DefaultTabGroupFirstSorter;
};
-namespace LLInitParam
-{
- template<>
- bool ParamCompare<LLUICtrl::commit_callback_t>::equals(
- const LLUICtrl::commit_callback_t &a,
- const LLUICtrl::commit_callback_t &b);
-
- template<>
- bool ParamCompare<LLUICtrl::enable_callback_t>::equals(
- const LLUICtrl::enable_callback_t &a,
- const LLUICtrl::enable_callback_t &b);
-
- template<>
- bool ParamCompare<LLUICtrl::focus_callback_t>::equals(
- const LLUICtrl::focus_callback_t &a,
- const LLUICtrl::focus_callback_t &b);
-}
+// Build time optimization, generate once in .cpp file
+#ifndef LLUICTRL_CPP
+extern template class LLUICtrl* LLView::getChild<class LLUICtrl>(
+ const std::string& name, BOOL recurse) const;
+#endif
#endif // LL_LLUICTRL_H
diff --git a/indra/llui/lluictrlfactory.cpp b/indra/llui/lluictrlfactory.cpp
index 24e4ad18e6..15a382660e 100644
--- a/indra/llui/lluictrlfactory.cpp
+++ b/indra/llui/lluictrlfactory.cpp
@@ -2,38 +2,35 @@
* @file lluictrlfactory.cpp
* @brief Factory class for creating UI controls
*
- * $LicenseInfo:firstyear=2003&license=viewergpl$
- *
- * Copyright (c) 2003-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2003&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "linden_common.h"
+#define LLUICTRLFACTORY_CPP
#include "lluictrlfactory.h"
+#include "llxmlnode.h"
+
#include <fstream>
#include <boost/tokenizer.hpp>
@@ -45,49 +42,13 @@
#include "llquaternion.h"
// this library includes
-#include "llbutton.h"
-#include "llcheckboxctrl.h"
-//#include "llcolorswatch.h"
-#include "llcombobox.h"
-#include "llcontrol.h"
-#include "lldir.h"
-#include "llevent.h"
#include "llfloater.h"
-#include "lliconctrl.h"
-#include "lllineeditor.h"
-#include "llmenugl.h"
-#include "llradiogroup.h"
-#include "llscrollcontainer.h"
-#include "llscrollingpanellist.h"
-#include "llscrolllistctrl.h"
-#include "llslider.h"
-#include "llsliderctrl.h"
-#include "llmultislider.h"
-#include "llmultisliderctrl.h"
-#include "llspinctrl.h"
-#include "lltabcontainer.h"
-#include "lltextbox.h"
-#include "lltexteditor.h"
-#include "llui.h"
-#include "llviewborder.h"
-
-const char XML_HEADER[] = "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\" ?>\n";
-
-const S32 HPAD = 4;
-const S32 VPAD = 4;
-const S32 FLOATER_H_MARGIN = 15;
-const S32 MIN_WIDGET_HEIGHT = 10;
LLFastTimer::DeclareTimer FTM_WIDGET_CONSTRUCTION("Widget Construction");
LLFastTimer::DeclareTimer FTM_INIT_FROM_PARAMS("Widget InitFromParams");
LLFastTimer::DeclareTimer FTM_WIDGET_SETUP("Widget Setup");
//-----------------------------------------------------------------------------
-// Register widgets that are purely data driven here so they get linked in
-#include "llstatview.h"
-static LLDefaultWidgetRegistry::Register<LLStatView> register_stat_view("stat_view");
-
-//-----------------------------------------------------------------------------
// UI Ctrl class for padding
class LLUICtrlLocate : public LLUICtrl
@@ -107,7 +68,10 @@ public:
};
-static LLDefaultWidgetRegistry::Register<LLUICtrlLocate> r1("locate");
+static LLDefaultChildRegistry::Register<LLUICtrlLocate> r1("locate");
+
+// Build time optimization, generate this once in .cpp file
+template class LLUICtrlFactory* LLSingleton<class LLUICtrlFactory>::getInstance();
//-----------------------------------------------------------------------------
// LLUICtrlFactory()
@@ -119,8 +83,9 @@ LLUICtrlFactory::LLUICtrlFactory()
LLUICtrlFactory::~LLUICtrlFactory()
{
- delete mDummyPanel;
- mDummyPanel = NULL;
+ // go ahead and leak mDummyPanel since this is static destructor time
+ //delete mDummyPanel;
+ //mDummyPanel = NULL;
}
void LLUICtrlFactory::loadWidgetTemplate(const std::string& widget_tag, LLInitParam::BaseBlock& block)
@@ -130,13 +95,18 @@ void LLUICtrlFactory::loadWidgetTemplate(const std::string& widget_tag, LLInitPa
if (LLUICtrlFactory::getLayeredXMLNode(filename, root_node))
{
- LLXUIParser::instance().readXUI(root_node, block);
+ LLUICtrlFactory::instance().pushFileName(filename);
+ LLXUIParser::instance().readXUI(root_node, block, filename);
+ LLUICtrlFactory::instance().popFileName();
}
}
+static LLFastTimer::DeclareTimer FTM_CREATE_CHILDREN("Create XUI Children");
+
//static
-void LLUICtrlFactory::createChildren(LLView* viewp, LLXMLNodePtr node, LLXMLNodePtr output_node)
+void LLUICtrlFactory::createChildren(LLView* viewp, LLXMLNodePtr node, const widget_registry_t& registry, LLXMLNodePtr output_node)
{
+ LLFastTimer ft(FTM_CREATE_CHILDREN);
if (node.isNull()) return;
for (LLXMLNodePtr child_node = node->getFirstChild(); child_node.notNull(); child_node = child_node->getNextSibling())
@@ -147,10 +117,22 @@ void LLUICtrlFactory::createChildren(LLView* viewp, LLXMLNodePtr node, LLXMLNode
outputChild = output_node->createChild("", FALSE);
}
- if (!instance().createFromXML(child_node, viewp, LLStringUtil::null, outputChild, viewp->getChildRegistry()))
+ if (!instance().createFromXML(child_node, viewp, LLStringUtil::null, registry, outputChild))
{
+ // child_node is not a valid child for the current parent
std::string child_name = std::string(child_node->getName()->mString);
- llwarns << "Could not create widget named " << child_node->getName()->mString << llendl;
+ if (LLDefaultChildRegistry::instance().getValue(child_name))
+ {
+ // This means that the registry assocaited with the parent widget does not have an entry
+ // for the child widget
+ // You might need to add something like:
+ // static ParentWidgetRegistry::Register<ChildWidgetType> register("child_widget_name");
+ llwarns << child_name << " is not a valid child of " << node->getName()->mString << llendl;
+ }
+ else
+ {
+ llwarns << "Could not create widget named " << child_node->getName()->mString << llendl;
+ }
}
if (outputChild && !outputChild->mChildren && outputChild->mAttributes.empty() && outputChild->getValue().empty())
@@ -161,7 +143,7 @@ void LLUICtrlFactory::createChildren(LLView* viewp, LLXMLNodePtr node, LLXMLNode
}
-LLFastTimer::DeclareTimer FTM_XML_PARSE("XML Reading/Parsing");
+static LLFastTimer::DeclareTimer FTM_XML_PARSE("XML Reading/Parsing");
//-----------------------------------------------------------------------------
// getLayeredXMLNode()
//-----------------------------------------------------------------------------
@@ -189,14 +171,14 @@ bool LLUICtrlFactory::getLocalizedXMLNode(const std::string &xui_filename, LLXML
}
}
-static LLFastTimer::DeclareTimer BUILD_FLOATERS("Build Floaters");
+static LLFastTimer::DeclareTimer FTM_BUILD_FLOATERS("Build Floaters");
//-----------------------------------------------------------------------------
// buildFloater()
//-----------------------------------------------------------------------------
-void LLUICtrlFactory::buildFloater(LLFloater* floaterp, const std::string& filename, BOOL open_floater, LLXMLNodePtr output_node)
+bool LLUICtrlFactory::buildFloater(LLFloater* floaterp, const std::string& filename, LLXMLNodePtr output_node)
{
- LLFastTimer timer(BUILD_FLOATERS);
+ LLFastTimer timer(FTM_BUILD_FLOATERS);
LLXMLNodePtr root;
//if exporting, only load the language being exported,
@@ -206,24 +188,26 @@ void LLUICtrlFactory::buildFloater(LLFloater* floaterp, const std::string& filen
if (!LLUICtrlFactory::getLocalizedXMLNode(filename, root))
{
llwarns << "Couldn't parse floater from: " << LLUI::getLocalizedSkinPath() + gDirUtilp->getDirDelimiter() + filename << llendl;
- return;
+ return false;
}
}
else if (!LLUICtrlFactory::getLayeredXMLNode(filename, root))
{
llwarns << "Couldn't parse floater from: " << LLUI::getSkinPath() + gDirUtilp->getDirDelimiter() + filename << llendl;
- return;
+ return false;
}
// root must be called floater
if( !(root->hasName("floater") || root->hasName("multi_floater")) )
{
llwarns << "Root node should be named floater in: " << filename << llendl;
- return;
+ return false;
}
-
+
+ bool res = true;
+
lldebugs << "Building floater " << filename << llendl;
- mFileNames.push_back(gDirUtilp->findSkinnedFilename(LLUI::getSkinPath(), filename));
+ pushFileName(filename);
{
if (!floaterp->getFactoryMap().empty())
{
@@ -234,12 +218,9 @@ void LLUICtrlFactory::buildFloater(LLFloater* floaterp, const std::string& filen
floaterp->getCommitCallbackRegistrar().pushScope();
floaterp->getEnableCallbackRegistrar().pushScope();
- floaterp->initFloaterXML(root, floaterp->getParent(), open_floater, output_node);
+ res = floaterp->initFloaterXML(root, floaterp->getParent(), filename, output_node);
- if (LLUI::sShowXUINames)
- {
- floaterp->setToolTip(filename);
- }
+ floaterp->setXMLFilename(filename);
floaterp->getCommitCallbackRegistrar().popScope();
floaterp->getEnableCallbackRegistrar().popScope();
@@ -249,14 +230,9 @@ void LLUICtrlFactory::buildFloater(LLFloater* floaterp, const std::string& filen
mFactoryStack.pop_front();
}
}
- mFileNames.pop_back();
-}
-
-LLFloater* LLUICtrlFactory::buildFloaterFromXML(const std::string& filename, BOOL open_floater)
-{
- LLFloater* floater = new LLFloater();
- buildFloater(floater, filename, open_floater);
- return floater;
+ popFileName();
+
+ return res;
}
//-----------------------------------------------------------------------------
@@ -267,14 +243,14 @@ S32 LLUICtrlFactory::saveToXML(LLView* viewp, const std::string& filename)
return 0;
}
-static LLFastTimer::DeclareTimer BUILD_PANELS("Build Panels");
+static LLFastTimer::DeclareTimer FTM_BUILD_PANELS("Build Panels");
//-----------------------------------------------------------------------------
// buildPanel()
//-----------------------------------------------------------------------------
BOOL LLUICtrlFactory::buildPanel(LLPanel* panelp, const std::string& filename, LLXMLNodePtr output_node)
{
- LLFastTimer timer(BUILD_PANELS);
+ LLFastTimer timer(FTM_BUILD_PANELS);
BOOL didPost = FALSE;
LLXMLNodePtr root;
@@ -303,7 +279,7 @@ BOOL LLUICtrlFactory::buildPanel(LLPanel* panelp, const std::string& filename, L
lldebugs << "Building panel " << filename << llendl;
- mFileNames.push_back(gDirUtilp->findSkinnedFilename(LLUI::getSkinPath(), filename));
+ pushFileName(filename);
{
if (!panelp->getFactoryMap().empty())
{
@@ -319,31 +295,28 @@ BOOL LLUICtrlFactory::buildPanel(LLPanel* panelp, const std::string& filename, L
panelp->getCommitCallbackRegistrar().popScope();
panelp->getEnableCallbackRegistrar().popScope();
- if (LLUI::sShowXUINames)
- {
- panelp->setToolTip(filename);
- }
+ panelp->setXMLFilename(filename);
if (!panelp->getFactoryMap().empty())
{
mFactoryStack.pop_front();
}
}
- mFileNames.pop_back();
+ popFileName();
return didPost;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
-LLFastTimer::DeclareTimer FTM_CREATE_FROM_XML("Create child widget");
+static LLFastTimer::DeclareTimer FTM_CREATE_FROM_XML("Create child widget");
-LLView *LLUICtrlFactory::createFromXML(LLXMLNodePtr node, LLView* parent, const std::string& filename, LLXMLNodePtr output_node, const widget_registry_t& registry)
+LLView *LLUICtrlFactory::createFromXML(LLXMLNodePtr node, LLView* parent, const std::string& filename, const widget_registry_t& registry, LLXMLNodePtr output_node)
{
LLFastTimer timer(FTM_CREATE_FROM_XML);
std::string ctrl_type = node->getName()->mString;
LLStringUtil::toLower(ctrl_type);
-
+
const LLWidgetCreatorFunc* funcp = registry.getValue(ctrl_type);
if (funcp == NULL)
{
@@ -360,10 +333,6 @@ LLView *LLUICtrlFactory::createFromXML(LLXMLNodePtr node, LLView* parent, const
parent = mDummyPanel;
}
LLView *view = (*funcp)(node, parent, output_node);
- if (LLUI::sShowXUINames && view && !filename.empty())
- {
- view->setToolTip(filename);
- }
return view;
}
@@ -391,6 +360,23 @@ LLPanel* LLUICtrlFactory::createFactoryPanel(const std::string& name)
return create<LLPanel>(panel_p);
}
+std::string LLUICtrlFactory::getCurFileName()
+{
+ return mFileNames.empty() ? "" : gDirUtilp->getWorkingDir() + gDirUtilp->getDirDelimiter() + mFileNames.back();
+}
+
+
+void LLUICtrlFactory::pushFileName(const std::string& name)
+{
+ mFileNames.push_back(gDirUtilp->findSkinnedFilename(LLUI::getSkinPath(), name));
+}
+
+void LLUICtrlFactory::popFileName()
+{
+ mFileNames.pop_back();
+}
+
+
//-----------------------------------------------------------------------------
//static
@@ -398,11 +384,11 @@ BOOL LLUICtrlFactory::getAttributeColor(LLXMLNodePtr node, const std::string& na
{
std::string colorstring;
BOOL res = node->getAttributeString(name.c_str(), colorstring);
- if (res && LLUI::sSettingGroups["color"])
+ if (res)
{
- if (LLUI::sSettingGroups["color"]->controlExists(colorstring))
+ if (LLUIColorTable::instance().colorExists(colorstring))
{
- color.setVec(LLUI::sSettingGroups["color"]->getColor(colorstring));
+ color.setVec(LLUIColorTable::instance().getColor(colorstring));
}
else
{
@@ -423,7 +409,7 @@ BOOL LLUICtrlFactory::getAttributeColor(LLXMLNodePtr node, const std::string& na
//static
void LLUICtrlFactory::setCtrlParent(LLView* view, LLView* parent, S32 tab_group)
{
- if (tab_group < 0) tab_group = parent->getLastTabGroup();
+ if (tab_group == S32_MAX) tab_group = parent->getLastTabGroup();
parent->addChild(view, tab_group);
}
@@ -448,647 +434,50 @@ void LLUICtrlFactory::popFactoryFunctions()
}
}
-const widget_registry_t& LLUICtrlFactory::getWidgetRegistry(LLView* viewp)
-{
- return viewp->getChildRegistry();
-}
-
-
-//
-// LLXUIParser
-//
-LLXUIParser::LLXUIParser()
-: mLastWriteGeneration(-1),
- mCurReadDepth(0)
-{
- registerParserFuncs<bool>(boost::bind(&LLXUIParser::readBoolValue, this, _1),
- boost::bind(&LLXUIParser::writeBoolValue, this, _1, _2));
- registerParserFuncs<std::string>(boost::bind(&LLXUIParser::readStringValue, this, _1),
- boost::bind(&LLXUIParser::writeStringValue, this, _1, _2));
- registerParserFuncs<U8>(boost::bind(&LLXUIParser::readU8Value, this, _1),
- boost::bind(&LLXUIParser::writeU8Value, this, _1, _2));
- registerParserFuncs<S8>(boost::bind(&LLXUIParser::readS8Value, this, _1),
- boost::bind(&LLXUIParser::writeS8Value, this, _1, _2));
- registerParserFuncs<U16>(boost::bind(&LLXUIParser::readU16Value, this, _1),
- boost::bind(&LLXUIParser::writeU16Value, this, _1, _2));
- registerParserFuncs<S16>(boost::bind(&LLXUIParser::readS16Value, this, _1),
- boost::bind(&LLXUIParser::writeS16Value, this, _1, _2));
- registerParserFuncs<U32>(boost::bind(&LLXUIParser::readU32Value, this, _1),
- boost::bind(&LLXUIParser::writeU32Value, this, _1, _2));
- registerParserFuncs<S32>(boost::bind(&LLXUIParser::readS32Value, this, _1),
- boost::bind(&LLXUIParser::writeS32Value, this, _1, _2));
- registerParserFuncs<F32>(boost::bind(&LLXUIParser::readF32Value, this, _1),
- boost::bind(&LLXUIParser::writeF32Value, this, _1, _2));
- registerParserFuncs<F64>(boost::bind(&LLXUIParser::readF64Value, this, _1),
- boost::bind(&LLXUIParser::writeF64Value, this, _1, _2));
- registerParserFuncs<LLColor4>(boost::bind(&LLXUIParser::readColor4Value, this, _1),
- boost::bind(&LLXUIParser::writeColor4Value, this, _1, _2));
- registerParserFuncs<LLUIColor>(boost::bind(&LLXUIParser::readUIColorValue, this, _1),
- boost::bind(&LLXUIParser::writeUIColorValue, this, _1, _2));
- registerParserFuncs<LLUUID>(boost::bind(&LLXUIParser::readUUIDValue, this, _1),
- boost::bind(&LLXUIParser::writeUUIDValue, this, _1, _2));
- registerParserFuncs<LLSD>(boost::bind(&LLXUIParser::readSDValue, this, _1),
- boost::bind(&LLXUIParser::writeSDValue, this, _1, _2));
-}
-
-static LLFastTimer::DeclareTimer PARSE_XUI("XUI Parsing");
-
-void LLXUIParser::readXUI(LLXMLNodePtr node, LLInitParam::BaseBlock& block, bool silent)
-{
- LLFastTimer timer(PARSE_XUI);
- mNameStack.clear();
- mCurReadDepth = 0;
- setParseSilently(silent);
-
- if (node.isNull())
- {
- parserWarning("Invalid node");
- }
- else
- {
- readXUIImpl(node, std::string(node->getName()->mString), block);
- }
-}
-
-void LLXUIParser::writeXUI(LLXMLNodePtr node, const LLInitParam::BaseBlock &block, const LLInitParam::BaseBlock* diff_block)
-{
- mLastWriteGeneration = -1;
- mWriteRootNode = node;
- block.serializeBlock(*this, Parser::name_stack_t(), diff_block);
-}
-
-// go from a stack of names to a specific XML node
-LLXMLNodePtr LLXUIParser::getNode(const name_stack_t& stack)
+//static
+void LLUICtrlFactory::copyName(LLXMLNodePtr src, LLXMLNodePtr dest)
{
- name_stack_t name_stack;
-
- for (name_stack_t::const_iterator it = stack.begin();
- it != stack.end();
- ++it)
- {
- if (!it->first.empty())
- {
- name_stack.push_back(*it);
- }
- }
-
- if (name_stack.empty() || mWriteRootNode.isNull()) return NULL;
-
- std::string attribute_name = name_stack.front().first;
-
- // heuristic to make font always attribute of parent node
- bool is_font = (attribute_name == "font");
- // XML spec says that attributes have their whitespace normalized
- // on parse: http://www.w3.org/TR/REC-xml/#AVNormalize
- // Therefore text-oriented widgets that might have carriage returns
- // have their values serialized as text contents, not the
- // initial_value attribute. JC
- if (attribute_name == "initial_value")
- {
- const char* root_node_name = mWriteRootNode->getName()->mString;
- if (!strcmp(root_node_name, "text") // LLTextBox
- || !strcmp(root_node_name, "text_editor")
- || !strcmp(root_node_name, "line_editor")) // for consistency
- {
- // writeStringValue will write to this node
- return mWriteRootNode;
- }
- }
-
- for (name_stack_t::const_iterator it = ++name_stack.begin();
- it != name_stack.end();
- ++it)
- {
- attribute_name += ".";
- attribute_name += it->first;
- }
-
- // *NOTE: <string> elements for translation need to have whitespace
- // preserved like "initial_value" above, however, the <string> node
- // becomes an attribute of the containing floater or panel.
- // Because all <string> elements must have a "name" attribute, and
- // "name" is parsed first, just put the value into the last written
- // child.
- if (attribute_name == "string.value")
- {
- // The caller of will shortly call writeStringValue(), which sets
- // this node's type to string, but we don't want to export type="string".
- // Set the default for this node to suppress the export.
- static LLXMLNodePtr default_node;
- if (default_node.isNull())
- {
- default_node = new LLXMLNode();
- // Force the node to have a string type
- default_node->setStringValue( std::string() );
- }
- mLastWrittenChild->setDefault(default_node);
- // mLastWrittenChild is the "string" node part of "string.value",
- // so the caller will call writeStringValue() into that node,
- // setting the node text contents.
- return mLastWrittenChild;
- }
-
- LLXMLNodePtr attribute_node;
-
- const char* attribute_cstr = attribute_name.c_str();
- if (name_stack.size() != 1
- && !is_font)
- {
- std::string child_node_name(mWriteRootNode->getName()->mString);
- child_node_name += ".";
- child_node_name += name_stack.front().first;
-
- LLXMLNodePtr child_node;
-
- if (mLastWriteGeneration == name_stack.front().second)
- {
- child_node = mLastWrittenChild;
- }
- else
- {
- mLastWriteGeneration = name_stack.front().second;
- child_node = mWriteRootNode->createChild(child_node_name.c_str(), false);
- }
-
- mLastWrittenChild = child_node;
-
- name_stack_t::const_iterator it = ++name_stack.begin();
- std::string short_attribute_name(it->first);
-
- for (++it;
- it != name_stack.end();
- ++it)
- {
- short_attribute_name += ".";
- short_attribute_name += it->first;
- }
-
- if (child_node->hasAttribute(short_attribute_name.c_str()))
- {
- llerrs << "Attribute " << short_attribute_name << " already exists!" << llendl;
- }
-
- attribute_node = child_node->createChild(short_attribute_name.c_str(), true);
- }
- else
- {
- if (mWriteRootNode->hasAttribute(attribute_cstr))
- {
- mWriteRootNode->getAttribute(attribute_cstr, attribute_node);
- }
- else
- {
- attribute_node = mWriteRootNode->createChild(attribute_name.c_str(), true);
- }
- }
-
- return attribute_node;
+ dest->setName(src->getName()->mString);
}
-
-bool LLXUIParser::readXUIImpl(LLXMLNodePtr nodep, const std::string& scope, LLInitParam::BaseBlock& block)
+// adds a widget and its param block to various registries
+//static
+void LLUICtrlFactory::registerWidget(const std::type_info* widget_type, const std::type_info* param_block_type, dummy_widget_creator_func_t creator_func, const std::string& tag)
{
- typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
- boost::char_separator<char> sep(".");
-
- bool values_parsed = false;
-
- // submit attributes for current node
- values_parsed |= readAttributes(nodep, block);
-
- // treat text contents of xml node as "value" parameter
- std::string text_contents = nodep->getSanitizedValue();
- if (!text_contents.empty())
+ // associate parameter block type with template .xml file
+ std::string* existing_tag = LLWidgetNameRegistry::instance().getValue(param_block_type);
+ if (existing_tag != NULL)
{
- mCurReadNode = nodep;
- mNameStack.push_back(std::make_pair(std::string("value"), newParseGeneration()));
- // child nodes are not necessarily valid parameters (could be a child widget)
- // so don't complain once we've recursed
- bool silent = mCurReadDepth > 0;
- block.submitValue(mNameStack, *this, silent);
- mNameStack.pop_back();
- }
-
- // then traverse children
- // child node must start with last name of parent node (our "scope")
- // for example: "<button><button.param nested_param1="foo"><param.nested_param2 nested_param3="bar"/></button.param></button>"
- // which equates to the following nesting:
- // button
- // param
- // nested_param1
- // nested_param2
- // nested_param3
- mCurReadDepth++;
- for(LLXMLNodePtr childp = nodep->getFirstChild(); childp.notNull();)
- {
- std::string child_name(childp->getName()->mString);
- S32 num_tokens_pushed = 0;
-
- // for non "dotted" child nodes check to see if child node maps to another widget type
- // and if not, treat as a child element of the current node
- // e.g. <button><rect left="10"/></button> will interpret <rect> as "button.rect"
- // since there is no widget named "rect"
- if (child_name.find(".") == std::string::npos)
- {
- mNameStack.push_back(std::make_pair(child_name, newParseGeneration()));
- num_tokens_pushed++;
- }
- else
- {
- // parse out "dotted" name into individual tokens
- tokenizer name_tokens(child_name, sep);
-
- tokenizer::iterator name_token_it = name_tokens.begin();
- if(name_token_it == name_tokens.end())
- {
- childp = childp->getNextSibling();
- continue;
- }
-
- // check for proper nesting
- if(!scope.empty() && *name_token_it != scope)
- {
- childp = childp->getNextSibling();
- continue;
- }
-
- // now ignore first token
- ++name_token_it;
-
- // copy remaining tokens on to our running token list
- for(tokenizer::iterator token_to_push = name_token_it; token_to_push != name_tokens.end(); ++token_to_push)
- {
- mNameStack.push_back(std::make_pair(*token_to_push, newParseGeneration()));
- num_tokens_pushed++;
- }
- }
-
- // recurse and visit children XML nodes
- if(readXUIImpl(childp, mNameStack.empty() ? scope : mNameStack.back().first, block))
+ if(*existing_tag != tag)
{
- // child node successfully parsed, remove from DOM
-
- values_parsed = true;
- LLXMLNodePtr node_to_remove = childp;
- childp = childp->getNextSibling();
-
- nodep->deleteChild(node_to_remove);
+ std::cerr << "Duplicate entry for T::Params, try creating empty param block in derived classes that inherit T::Params" << std::endl;
+ // forcing crash here
+ char* foo = 0;
+ *foo = 1;
}
else
{
- childp = childp->getNextSibling();
- }
-
- while(num_tokens_pushed-- > 0)
- {
- mNameStack.pop_back();
- }
- }
- mCurReadDepth--;
- return values_parsed;
-}
-
-bool LLXUIParser::readAttributes(LLXMLNodePtr nodep, LLInitParam::BaseBlock& block)
-{
- typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
- boost::char_separator<char> sep(".");
-
- bool any_parsed = false;
-
- for(LLXMLAttribList::const_iterator attribute_it = nodep->mAttributes.begin();
- attribute_it != nodep->mAttributes.end();
- ++attribute_it)
- {
- S32 num_tokens_pushed = 0;
- std::string attribute_name(attribute_it->first->mString);
- mCurReadNode = attribute_it->second;
-
- tokenizer name_tokens(attribute_name, sep);
- // copy remaining tokens on to our running token list
- for(tokenizer::iterator token_to_push = name_tokens.begin(); token_to_push != name_tokens.end(); ++token_to_push)
- {
- mNameStack.push_back(std::make_pair(*token_to_push, newParseGeneration()));
- num_tokens_pushed++;
- }
-
- // child nodes are not necessarily valid attributes, so don't complain once we've recursed
- bool silent = mCurReadDepth > 0;
- any_parsed |= block.submitValue(mNameStack, *this, silent);
-
- while(num_tokens_pushed-- > 0)
- {
- mNameStack.pop_back();
+ // widget already registered
+ return;
}
}
-
- return any_parsed;
-}
-
-bool LLXUIParser::readBoolValue(void* val_ptr)
-{
- S32 value;
- bool success = mCurReadNode->getBoolValue(1, &value);
- *((bool*)val_ptr) = (value != FALSE);
- return success;
-}
-
-bool LLXUIParser::writeBoolValue(const void* val_ptr, const name_stack_t& stack)
-{
- LLXMLNodePtr node = getNode(stack);
- if (node.notNull())
- {
- node->setBoolValue(*((bool*)val_ptr));
- return true;
- }
- return false;
-}
-
-bool LLXUIParser::readStringValue(void* val_ptr)
-{
- *((std::string*)val_ptr) = mCurReadNode->getSanitizedValue();
- return true;
-}
-
-bool LLXUIParser::writeStringValue(const void* val_ptr, const name_stack_t& stack)
-{
- LLXMLNodePtr node = getNode(stack);
- if (node.notNull())
- {
- node->setStringValue(*((std::string*)val_ptr));
- return true;
- }
- return false;
-}
-
-bool LLXUIParser::readU8Value(void* val_ptr)
-{
- return mCurReadNode->getByteValue(1, (U8*)val_ptr);
-}
-
-bool LLXUIParser::writeU8Value(const void* val_ptr, const name_stack_t& stack)
-{
- LLXMLNodePtr node = getNode(stack);
- if (node.notNull())
- {
- node->setUnsignedValue(*((U8*)val_ptr));
- return true;
- }
- return false;
-}
-
-bool LLXUIParser::readS8Value(void* val_ptr)
-{
- S32 value;
- if(mCurReadNode->getIntValue(1, &value))
- {
- *((S8*)val_ptr) = value;
- return true;
- }
- return false;
-}
-
-bool LLXUIParser::writeS8Value(const void* val_ptr, const name_stack_t& stack)
-{
- LLXMLNodePtr node = getNode(stack);
- if (node.notNull())
- {
- node->setIntValue(*((S8*)val_ptr));
- return true;
- }
- return false;
-}
-
-bool LLXUIParser::readU16Value(void* val_ptr)
-{
- U32 value;
- if(mCurReadNode->getUnsignedValue(1, &value))
- {
- *((U16*)val_ptr) = value;
- return true;
- }
- return false;
-}
-
-bool LLXUIParser::writeU16Value(const void* val_ptr, const name_stack_t& stack)
-{
- LLXMLNodePtr node = getNode(stack);
- if (node.notNull())
- {
- node->setUnsignedValue(*((U16*)val_ptr));
- return true;
- }
- return false;
-}
-
-bool LLXUIParser::readS16Value(void* val_ptr)
-{
- S32 value;
- if(mCurReadNode->getIntValue(1, &value))
- {
- *((S16*)val_ptr) = value;
- return true;
- }
- return false;
-}
-
-bool LLXUIParser::writeS16Value(const void* val_ptr, const name_stack_t& stack)
-{
- LLXMLNodePtr node = getNode(stack);
- if (node.notNull())
- {
- node->setIntValue(*((S16*)val_ptr));
- return true;
- }
- return false;
-}
-
-bool LLXUIParser::readU32Value(void* val_ptr)
-{
- return mCurReadNode->getUnsignedValue(1, (U32*)val_ptr);
-}
-
-bool LLXUIParser::writeU32Value(const void* val_ptr, const name_stack_t& stack)
-{
- LLXMLNodePtr node = getNode(stack);
- if (node.notNull())
- {
- node->setUnsignedValue(*((U32*)val_ptr));
- return true;
- }
- return false;
-}
-
-bool LLXUIParser::readS32Value(void* val_ptr)
-{
- return mCurReadNode->getIntValue(1, (S32*)val_ptr);
-}
-
-bool LLXUIParser::writeS32Value(const void* val_ptr, const name_stack_t& stack)
-{
- LLXMLNodePtr node = getNode(stack);
- if (node.notNull())
- {
- node->setIntValue(*((S32*)val_ptr));
- return true;
- }
- return false;
-}
-
-bool LLXUIParser::readF32Value(void* val_ptr)
-{
- return mCurReadNode->getFloatValue(1, (F32*)val_ptr);
-}
-
-bool LLXUIParser::writeF32Value(const void* val_ptr, const name_stack_t& stack)
-{
- LLXMLNodePtr node = getNode(stack);
- if (node.notNull())
- {
- node->setFloatValue(*((F32*)val_ptr));
- return true;
- }
- return false;
+ LLWidgetNameRegistry::instance().defaultRegistrar().add(param_block_type, tag);
+ // associate widget type with factory function
+ LLDefaultWidgetRegistry::instance().defaultRegistrar().add(widget_type, creator_func);
+ //FIXME: comment this in when working on schema generation
+ //LLWidgetTypeRegistry::instance().defaultRegistrar().add(tag, widget_type);
+ //LLDefaultParamBlockRegistry::instance().defaultRegistrar().add(widget_type, &getEmptyParamBlock<T>);
}
-bool LLXUIParser::readF64Value(void* val_ptr)
-{
- return mCurReadNode->getDoubleValue(1, (F64*)val_ptr);
-}
-
-bool LLXUIParser::writeF64Value(const void* val_ptr, const name_stack_t& stack)
-{
- LLXMLNodePtr node = getNode(stack);
- if (node.notNull())
- {
- node->setDoubleValue(*((F64*)val_ptr));
- return true;
- }
- return false;
-}
-
-bool LLXUIParser::readColor4Value(void* val_ptr)
-{
- LLColor4* colorp = (LLColor4*)val_ptr;
- if(mCurReadNode->getFloatValue(4, colorp->mV) >= 3)
- {
- return true;
- }
-
- return false;
-}
-
-bool LLXUIParser::writeColor4Value(const void* val_ptr, const name_stack_t& stack)
-{
- LLXMLNodePtr node = getNode(stack);
- if (node.notNull())
- {
- LLColor4 color = *((LLColor4*)val_ptr);
- node->setFloatValue(4, color.mV);
- return true;
- }
- return false;
-}
-
-bool LLXUIParser::readUIColorValue(void* val_ptr)
-{
- LLUIColor* param = (LLUIColor*)val_ptr;
- LLColor4 color;
- bool success = mCurReadNode->getFloatValue(4, color.mV) >= 3;
- if (success)
- {
- param->set(color);
- return true;
- }
- return false;
-}
-
-bool LLXUIParser::writeUIColorValue(const void* val_ptr, const name_stack_t& stack)
-{
- LLXMLNodePtr node = getNode(stack);
- if (node.notNull())
- {
- LLUIColor color = *((LLUIColor*)val_ptr);
- //RN: don't write out the color that is represented by a function
- // rely on param block exporting to get the reference to the color settings
- if (color.isUsingFunction()) return false;
- node->setFloatValue(4, color.get().mV);
- return true;
- }
- return false;
-}
-
-bool LLXUIParser::readUUIDValue(void* val_ptr)
-{
- LLUUID temp_id;
- // LLUUID::set is destructive, so use temporary value
- if (temp_id.set(mCurReadNode->getSanitizedValue()))
- {
- *(LLUUID*)(val_ptr) = temp_id;
- return true;
- }
- return false;
-}
-
-bool LLXUIParser::writeUUIDValue(const void* val_ptr, const name_stack_t& stack)
-{
- LLXMLNodePtr node = getNode(stack);
- if (node.notNull())
- {
- node->setStringValue(((LLUUID*)val_ptr)->asString());
- return true;
- }
- return false;
-}
-
-bool LLXUIParser::readSDValue(void* val_ptr)
-{
- *((LLSD*)val_ptr) = LLSD(mCurReadNode->getSanitizedValue());
- return true;
-}
-
-bool LLXUIParser::writeSDValue(const void* val_ptr, const name_stack_t& stack)
-{
- LLXMLNodePtr node = getNode(stack);
- if (node.notNull())
- {
- node->setStringValue(((LLSD*)val_ptr)->asString());
- return true;
- }
- return false;
-}
-
-/*virtual*/ std::string LLXUIParser::getCurrentElementName()
+//static
+dummy_widget_creator_func_t* LLUICtrlFactory::getDefaultWidgetFunc(const std::type_info* widget_type)
{
- std::string full_name;
- for (name_stack_t::iterator it = mNameStack.begin();
- it != mNameStack.end();
- ++it)
- {
- full_name += it->first + "."; // build up dotted names: "button.param.nestedparam."
- }
-
- return full_name;
+ return LLDefaultWidgetRegistry::instance().getValue(widget_type);
}
-void LLXUIParser::parserWarning(const std::string& message)
+//static
+const std::string* LLUICtrlFactory::getWidgetTag(const std::type_info* widget_type)
{
-#ifdef LL_WINDOWS
- // use Visual Studo friendly formatting of output message for easy access to originating xml
- llutf16string utf16str = utf8str_to_utf16str(llformat("%s(%d):\t%s", LLUICtrlFactory::getInstance()->getCurFileName().c_str(), mCurReadNode->getLineNumber(), message.c_str()).c_str());
- utf16str += '\n';
- OutputDebugString(utf16str.c_str());
-#else
- Parser::parserWarning(message);
-#endif
+ return LLWidgetNameRegistry::instance().getValue(widget_type);
}
-void LLXUIParser::parserError(const std::string& message)
-{
-#ifdef LL_WINDOWS
- llutf16string utf16str = utf8str_to_utf16str(llformat("%s(%d):\t%s", LLUICtrlFactory::getInstance()->getCurFileName().c_str(), mCurReadNode->getLineNumber(), message.c_str()).c_str());
- utf16str += '\n';
- OutputDebugString(utf16str.c_str());
-#else
- Parser::parserError(message);
-#endif
-}
diff --git a/indra/llui/lluictrlfactory.h b/indra/llui/lluictrlfactory.h
index 4045022c8e..58ec5d8387 100644
--- a/indra/llui/lluictrlfactory.h
+++ b/indra/llui/lluictrlfactory.h
@@ -2,31 +2,25 @@
* @file lluictrlfactory.h
* @brief Factory class for creating UI controls
*
- * $LicenseInfo:firstyear=2003&license=viewergpl$
- *
- * Copyright (c) 2003-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2003&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -35,96 +29,39 @@
#include "llcallbackmap.h"
#include "llinitparam.h"
-#include "llxmlnode.h"
+#include "llregistry.h"
+#include "v4color.h"
#include "llfasttimer.h"
+#include "llxuiparser.h"
+
#include <boost/function.hpp>
#include <iosfwd>
#include <stack>
+#include <set>
class LLPanel;
class LLFloater;
class LLView;
-class LLXUIParser : public LLInitParam::Parser, public LLSingleton<LLXUIParser>
-{
-LOG_CLASS(LLXUIParser);
-
-protected:
- LLXUIParser();
- friend class LLSingleton<LLXUIParser>;
-public:
- typedef LLInitParam::Parser::name_stack_t name_stack_t;
-
- /*virtual*/ std::string getCurrentElementName();
- /*virtual*/ void parserWarning(const std::string& message);
- /*virtual*/ void parserError(const std::string& message);
-
- void readXUI(LLXMLNodePtr node, LLInitParam::BaseBlock& block, bool silent=false);
- void writeXUI(LLXMLNodePtr node, const LLInitParam::BaseBlock& block, const LLInitParam::BaseBlock* diff_block = NULL);
-
-private:
- typedef std::list<std::pair<std::string, bool> > token_list_t;
-
- bool readXUIImpl(LLXMLNodePtr node, const std::string& scope, LLInitParam::BaseBlock& block);
- bool readAttributes(LLXMLNodePtr nodep, LLInitParam::BaseBlock& block);
-
- //reader helper functions
- bool readBoolValue(void* val_ptr);
- bool readStringValue(void* val_ptr);
- bool readU8Value(void* val_ptr);
- bool readS8Value(void* val_ptr);
- bool readU16Value(void* val_ptr);
- bool readS16Value(void* val_ptr);
- bool readU32Value(void* val_ptr);
- bool readS32Value(void* val_ptr);
- bool readF32Value(void* val_ptr);
- bool readF64Value(void* val_ptr);
- bool readColor4Value(void* val_ptr);
- bool readUIColorValue(void* val_ptr);
- bool readUUIDValue(void* val_ptr);
- bool readSDValue(void* val_ptr);
-
- //writer helper functions
- bool writeBoolValue(const void* val_ptr, const name_stack_t&);
- bool writeStringValue(const void* val_ptr, const name_stack_t&);
- bool writeU8Value(const void* val_ptr, const name_stack_t&);
- bool writeS8Value(const void* val_ptr, const name_stack_t&);
- bool writeU16Value(const void* val_ptr, const name_stack_t&);
- bool writeS16Value(const void* val_ptr, const name_stack_t&);
- bool writeU32Value(const void* val_ptr, const name_stack_t&);
- bool writeS32Value(const void* val_ptr, const name_stack_t&);
- bool writeF32Value(const void* val_ptr, const name_stack_t&);
- bool writeF64Value(const void* val_ptr, const name_stack_t&);
- bool writeColor4Value(const void* val_ptr, const name_stack_t&);
- bool writeUIColorValue(const void* val_ptr, const name_stack_t&);
- bool writeUUIDValue(const void* val_ptr, const name_stack_t&);
- bool writeSDValue(const void* val_ptr, const name_stack_t&);
-
- LLXMLNodePtr getNode(const name_stack_t& stack);
-private:
- Parser::name_stack_t mNameStack;
- LLXMLNodePtr mCurReadNode;
- // Root of the widget XML sub-tree, for example, "line_editor"
- LLXMLNodePtr mWriteRootNode;
- S32 mLastWriteGeneration;
- LLXMLNodePtr mLastWrittenChild;
- S32 mCurReadDepth;
+// sort functor for typeid maps
+struct LLCompareTypeID
+{
+ bool operator()(const std::type_info* lhs, const std::type_info* rhs) const
+ {
+ return lhs->before(*rhs);
+ }
};
-// global static instance for registering all widget types
-typedef boost::function<LLView* (LLXMLNodePtr node, LLView *parent, LLXMLNodePtr output_node)> LLWidgetCreatorFunc;
-
-typedef LLRegistry<std::string, LLWidgetCreatorFunc> widget_registry_t;
-
+// lookup widget constructor funcs by widget name
template <typename DERIVED_TYPE>
-class LLWidgetRegistry : public LLRegistrySingleton<std::string, LLWidgetCreatorFunc, DERIVED_TYPE>
+class LLChildRegistry : public LLRegistrySingleton<std::string, LLWidgetCreatorFunc, DERIVED_TYPE>
{
public:
typedef LLRegistrySingleton<std::string, LLWidgetCreatorFunc, DERIVED_TYPE> super_t;
// local static instance for registering a particular widget
- template<typename T, typename PARAM_BLOCK = typename T::Params>
+ template<typename T>
class Register : public super_t::StaticRegistrar
{
public:
@@ -133,35 +70,43 @@ public:
};
protected:
- LLWidgetRegistry() {}
+ LLChildRegistry() {}
};
-class LLDefaultWidgetRegistry : public LLWidgetRegistry<LLDefaultWidgetRegistry>
+class LLDefaultChildRegistry : public LLChildRegistry<LLDefaultChildRegistry>
{
protected:
- LLDefaultWidgetRegistry() {}
- friend class LLSingleton<LLDefaultWidgetRegistry>;
-};
-
-struct LLCompareTypeID
-{
- bool operator()(const std::type_info* lhs, const std::type_info* rhs) const
- {
- return lhs->before(*rhs);
- }
+ LLDefaultChildRegistry(){}
+ friend class LLSingleton<LLDefaultChildRegistry>;
};
+// lookup widget name by type
+class LLWidgetNameRegistry
+: public LLRegistrySingleton<const std::type_info*, std::string, LLWidgetNameRegistry , LLCompareTypeID>
+{};
-class LLWidgetTemplateRegistry
-: public LLRegistrySingleton<const std::type_info*, std::string, LLWidgetTemplateRegistry, LLCompareTypeID>
-{
+// lookup factory functions for default widget instances by widget type
+typedef LLView* (*dummy_widget_creator_func_t)(const std::string&);
+class LLDefaultWidgetRegistry
+: public LLRegistrySingleton<const std::type_info*, dummy_widget_creator_func_t, LLDefaultWidgetRegistry, LLCompareTypeID>
+{};
-};
+// lookup function for generating empty param block by widget type
+// this is used for schema generation
+//typedef const LLInitParam::BaseBlock& (*empty_param_block_func_t)();
+//class LLDefaultParamBlockRegistry
+//: public LLRegistrySingleton<const std::type_info*, empty_param_block_func_t, LLDefaultParamBlockRegistry, LLCompareTypeID>
+//{};
extern LLFastTimer::DeclareTimer FTM_WIDGET_SETUP;
extern LLFastTimer::DeclareTimer FTM_WIDGET_CONSTRUCTION;
extern LLFastTimer::DeclareTimer FTM_INIT_FROM_PARAMS;
+// Build time optimization, generate this once in .cpp file
+#ifndef LLUICTRLFACTORY_CPP
+extern template class LLUICtrlFactory* LLSingleton<class LLUICtrlFactory>::getInstance();
+#endif
+
class LLUICtrlFactory : public LLSingleton<LLUICtrlFactory>
{
private:
@@ -170,26 +115,26 @@ private:
~LLUICtrlFactory();
// only partial specialization allowed in inner classes, so use extra dummy parameter
- template <typename T, int DUMMY>
- class ParamDefaults : public LLSingleton<ParamDefaults<T, DUMMY> >
+ template <typename PARAM_BLOCK, int DUMMY>
+ class ParamDefaults : public LLSingleton<ParamDefaults<PARAM_BLOCK, DUMMY> >
{
public:
ParamDefaults()
{
// recursively initialize from base class param block
- ((typename T::base_block_t&)mPrototype).fillFrom(ParamDefaults<typename T::base_block_t, DUMMY>::instance().get());
+ ((typename PARAM_BLOCK::base_block_t&)mPrototype).fillFrom(ParamDefaults<typename PARAM_BLOCK::base_block_t, DUMMY>::instance().get());
// after initializing base classes, look up template file for this param block
- std::string* param_block_tag = LLWidgetTemplateRegistry::instance().getValue(&typeid(T));
+ const std::string* param_block_tag = getWidgetTag(&typeid(PARAM_BLOCK));
if (param_block_tag)
{
LLUICtrlFactory::loadWidgetTemplate(*param_block_tag, mPrototype);
}
}
- const T& get() { return mPrototype; }
+ const PARAM_BLOCK& get() { return mPrototype; }
private:
- T mPrototype;
+ PARAM_BLOCK mPrototype;
};
// base case for recursion, there are NO base classes of LLInitParam::BaseBlock
@@ -204,22 +149,24 @@ private:
public:
+ // get default parameter block for widget of a specific type
template<typename T>
- static const T& getDefaultParams()
+ static const typename T::Params& getDefaultParams()
{
//#pragma message("Generating ParamDefaults")
- return ParamDefaults<T, 0>::instance().get();
+ return ParamDefaults<typename T::Params, 0>::instance().get();
}
- void buildFloater(LLFloater* floaterp, const std::string &filename, BOOL open_floater = TRUE, LLXMLNodePtr output_node = NULL);
- LLFloater* buildFloaterFromXML(const std::string& filename, BOOL open_floater = TRUE);
+ bool buildFloater(LLFloater* floaterp, const std::string &filename, LLXMLNodePtr output_node);
BOOL buildPanel(LLPanel* panelp, const std::string &filename, LLXMLNodePtr output_node = NULL);
// Does what you want for LLFloaters and LLPanels
// Returns 0 on success
S32 saveToXML(LLView* viewp, const std::string& filename);
- std::string getCurFileName() { return mFileNames.empty() ? "" : mFileNames.back(); }
+ std::string getCurFileName();
+ void pushFileName(const std::string& name);
+ void popFileName();
static BOOL getAttributeColor(LLXMLNodePtr node, const std::string& name, LLColor4& color);
@@ -229,35 +176,56 @@ public:
void popFactoryFunctions();
template<typename T>
- static T* create(typename T::Params& params, LLView* parent = NULL)
+ static T* createWidget(const typename T::Params& params, LLView* parent = NULL)
{
- //#pragma message("Generating LLUICtrlFactory::create")
- params.fillFrom(ParamDefaults<typename T::Params, 0>::instance().get());
- //S32 foo = "test";
+ T* widget = NULL;
if (!params.validateBlock())
{
llwarns << getInstance()->getCurFileName() << ": Invalid parameter block for " << typeid(T).name() << llendl;
+ //return NULL;
+ }
+
+ {
+ LLFastTimer timer(FTM_WIDGET_CONSTRUCTION);
+ widget = new T(params);
}
- T* widget = new T(params);
- widget->initFromParams(params);
+ {
+ LLFastTimer timer(FTM_INIT_FROM_PARAMS);
+ widget->initFromParams(params);
+ }
+
if (parent)
- widget->setParent(parent);
+ {
+ S32 tab_group = params.tab_group.isProvided() ? params.tab_group() : S32_MAX;
+ setCtrlParent(widget, parent, tab_group);
+ }
return widget;
}
- LLView* createFromXML(LLXMLNodePtr node, LLView* parent, const std::string& filename, LLXMLNodePtr output_node, const widget_registry_t& );
+ template<typename T>
+ static T* create(typename T::Params& params, LLView* parent = NULL)
+ {
+ params.fillFrom(ParamDefaults<typename T::Params, 0>::instance().get());
+
+ T* widget = createWidget<T>(params, parent);
+ if (widget)
+ {
+ widget->postBuild();
+ }
+
+ return widget;
+ }
+
+ LLView* createFromXML(LLXMLNodePtr node, LLView* parent, const std::string& filename, const widget_registry_t&, LLXMLNodePtr output_node );
- static const widget_registry_t& getWidgetRegistry(LLView*);
-
template<typename T>
- static T* createFromFile(const std::string &filename, LLView *parent, LLXMLNodePtr output_node = NULL)
+ static T* createFromFile(const std::string &filename, LLView *parent, const widget_registry_t& registry, LLXMLNodePtr output_node = NULL)
{
- //#pragma message("Generating LLUICtrlFactory::createFromFile")
T* widget = NULL;
std::string skinned_filename = findSkinnedFilename(filename);
- getInstance()->mFileNames.push_back(skinned_filename);
+ instance().pushFileName(filename);
{
LLXMLNodePtr root_node;
@@ -277,7 +245,7 @@ public:
goto fail;
}
- LLView* view = getInstance()->createFromXML(root_node, parent, filename, output_node, getWidgetRegistry(parent));
+ LLView* view = getInstance()->createFromXML(root_node, parent, filename, registry, output_node);
if (view)
{
widget = dynamic_cast<T*>(view);
@@ -291,77 +259,68 @@ public:
}
}
fail:
- getInstance()->mFileNames.pop_back();
+ instance().popFileName();
return widget;
}
+ template<class T>
+ static T* getDefaultWidget(const std::string& name)
+ {
+ dummy_widget_creator_func_t* dummy_func = getDefaultWidgetFunc(&typeid(T));
+ return dummy_func ? dynamic_cast<T*>((*dummy_func)(name)) : NULL;
+ }
+
template <class T>
- static T* createDummyWidget(const std::string& name)
+ static LLView* createDefaultWidget(const std::string& name)
{
- //#pragma message("Generating LLUICtrlFactory::createDummyWidget")
typename T::Params params;
params.name(name);
return create<T>(params);
}
- template<typename T, typename PARAM_BLOCK>
+ static void copyName(LLXMLNodePtr src, LLXMLNodePtr dest);
+
+ template<typename T>
static T* defaultBuilder(LLXMLNodePtr node, LLView *parent, LLXMLNodePtr output_node)
{
LLFastTimer timer(FTM_WIDGET_SETUP);
- //#pragma message("Generating LLUICtrlFactory::defaultBuilder")
- PARAM_BLOCK params(getDefaultParams<PARAM_BLOCK>());
+ typename T::Params params(getDefaultParams<T>());
- LLXUIParser::instance().readXUI(node, params);
+ LLXUIParser::instance().readXUI(node, params, LLUICtrlFactory::getInstance()->getCurFileName());
if (output_node)
{
// We always want to output top-left coordinates
- PARAM_BLOCK output_params(params);
+ typename T::Params output_params(params);
T::setupParamsForExport(output_params, parent);
// Export only the differences between this any default params
- PARAM_BLOCK default_params(getDefaultParams<PARAM_BLOCK>());
- output_node->setName(node->getName()->mString);
+ typename T::Params default_params(getDefaultParams<T>());
+ copyName(node, output_node);
LLXUIParser::instance().writeXUI(
output_node, output_params, &default_params);
}
// Apply layout transformations, usually munging rect
- T::setupParams(params, parent);
+ params.from_xui = true;
+ T::applyXUILayout(params, parent);
+ T* widget = createWidget<T>(params, parent);
- if (!params.validateBlock())
- {
- llwarns << getInstance()->getCurFileName() << ": Invalid parameter block for " << typeid(T).name() << llendl;
- }
- T* widget;
- {
- LLFastTimer timer(FTM_WIDGET_CONSTRUCTION);
- widget = new T(params);
- }
- {
- LLFastTimer timer(FTM_INIT_FROM_PARAMS);
- widget->initFromParams(params);
- }
-
- if (parent)
- {
- S32 tab_group = params.tab_group.isProvided() ? params.tab_group() : -1;
- setCtrlParent(widget, parent, tab_group);
- }
+ typedef typename T::child_registry_t registry_t;
- createChildren(widget, node, output_node);
+ createChildren(widget, node, registry_t::instance(), output_node);
- if (!widget->postBuild())
+ if (widget && !widget->postBuild())
{
delete widget;
- return NULL;
+ widget = NULL;
}
return widget;
}
- static void createChildren(LLView* viewp, LLXMLNodePtr node, LLXMLNodePtr output_node = NULL);
+ static void createChildren(LLView* viewp, LLXMLNodePtr node, const widget_registry_t&, LLXMLNodePtr output_node = NULL);
static bool getLayeredXMLNode(const std::string &filename, LLXMLNodePtr& root);
@@ -369,8 +328,16 @@ fail:
static void loadWidgetTemplate(const std::string& widget_tag, LLInitParam::BaseBlock& block);
+ // helper function for adding widget type info to various registries
+ static void registerWidget(const std::type_info* widget_type, const std::type_info* param_block_type, dummy_widget_creator_func_t creator_func, const std::string& tag);
+
private:
- //static void setCtrlValue(LLView* view, LLXMLNodePtr node);
+ // return default widget instance factory func for a given type
+ static dummy_widget_creator_func_t* getDefaultWidgetFunc(const std::type_info* widget_type);
+
+ static const std::string* getWidgetTag(const std::type_info* widget_type);
+
+ // this exists to get around dependency on llview
static void setCtrlParent(LLView* view, LLView* parent, S32 tab_group);
// Avoid directly using LLUI and LLDir in the template code
@@ -383,18 +350,30 @@ private:
std::vector<std::string> mFileNames;
};
+template<typename T>
+const LLInitParam::BaseBlock& getEmptyParamBlock()
+{
+ static typename T::Params params;
+ return params;
+}
+
// this is here to make gcc happy with reference to LLUICtrlFactory
template<typename DERIVED>
-template<typename T, typename PARAM_BLOCK>
-LLWidgetRegistry<DERIVED>::Register<T, PARAM_BLOCK>::Register(const char* tag, LLWidgetCreatorFunc func)
-: LLWidgetRegistry<DERIVED>::StaticRegistrar(tag, func.empty() ? (LLWidgetCreatorFunc)&LLUICtrlFactory::defaultBuilder<T, PARAM_BLOCK> : func)
+template<typename T>
+LLChildRegistry<DERIVED>::Register<T>::Register(const char* tag, LLWidgetCreatorFunc func)
+: LLChildRegistry<DERIVED>::StaticRegistrar(tag, func.empty() ? (LLWidgetCreatorFunc)&LLUICtrlFactory::defaultBuilder<T> : func)
{
- //FIXME: inventory_panel will register itself with LLPanel::Params but it does have its own params...:(
- LLWidgetTemplateRegistry::instance().defaultRegistrar().add(&typeid(PARAM_BLOCK), tag);
+ // add this widget to various registries
+ LLUICtrlFactory::instance().registerWidget(&typeid(T), &typeid(typename T::Params), &LLUICtrlFactory::createDefaultWidget<T>, tag);
+
+ // since registry_t depends on T, do this in line here
+ // TODO: uncomment this for schema generation
+ //typedef typename T::child_registry_t registry_t;
+ //LLChildRegistryRegistry::instance().defaultRegistrar().add(&typeid(T), registry_t::instance());
}
-typedef boost::function<LLPanel* (void)> LLPannelClassCreatorFunc;
+typedef boost::function<LLPanel* (void)> LLPanelClassCreatorFunc;
// local static instance for registering a particular panel class
@@ -403,15 +382,15 @@ class LLRegisterPanelClass
{
public:
// reigister with either the provided builder, or the generic templated builder
- void addPanelClass(const std::string& tag,LLPannelClassCreatorFunc func)
+ void addPanelClass(const std::string& tag,LLPanelClassCreatorFunc func)
{
- mPannelClassesNames[tag] = func;
+ mPanelClassesNames[tag] = func;
}
LLPanel* createPanelClass(const std::string& tag)
{
- param_name_map_t::iterator iT = mPannelClassesNames.find(tag);
- if(iT == mPannelClassesNames.end())
+ param_name_map_t::iterator iT = mPanelClassesNames.find(tag);
+ if(iT == mPanelClassesNames.end())
return 0;
return iT->second();
}
@@ -423,9 +402,9 @@ public:
}
private:
- typedef std::map< std::string, LLPannelClassCreatorFunc> param_name_map_t;
+ typedef std::map< std::string, LLPanelClassCreatorFunc> param_name_map_t;
- param_name_map_t mPannelClassesNames;
+ param_name_map_t mPanelClassesNames;
};
diff --git a/indra/llui/lluifwd.h b/indra/llui/lluifwd.h
index f99bb39fdd..a68629a091 100644
--- a/indra/llui/lluifwd.h
+++ b/indra/llui/lluifwd.h
@@ -2,31 +2,25 @@
* @file lluifwd.h
* @brief Forward declarations of common LLUI widget types.
*
- * $LicenseInfo:firstyear=2007&license=viewergpl$
- *
- * Copyright (c) 2007-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -39,6 +33,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 8e0de0cb0c..1ffad4806e 100644
--- a/indra/llui/lluiimage.cpp
+++ b/indra/llui/lluiimage.cpp
@@ -2,31 +2,25 @@
* @file lluiimage.cpp
* @brief UI implementation
*
- * $LicenseInfo:firstyear=2007&license=viewergpl$
- *
- * Copyright (c) 2007-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -39,18 +33,20 @@
#include "lluiimage.h"
#include "llui.h"
-LLUIImage::LLUIImage(const std::string& name, LLPointer<LLImageGL> image) :
- mName(name),
- mImage(image),
- mScaleRegion(0.f, 1.f, 1.f, 0.f),
- mClipRegion(0.f, 1.f, 1.f, 0.f),
- mUniformScaling(TRUE),
- mNoClip(TRUE)
+LLUIImage::LLUIImage(const std::string& name, LLPointer<LLTexture> image)
+: mName(name),
+ mImage(image),
+ mScaleRegion(0.f, 1.f, 1.f, 0.f),
+ mClipRegion(0.f, 1.f, 1.f, 0.f),
+ mUniformScaling(TRUE),
+ mNoClip(TRUE),
+ mImageLoaded(NULL)
{
}
LLUIImage::~LLUIImage()
{
+ delete mImageLoaded;
}
void LLUIImage::setClipRegion(const LLRectf& region)
@@ -74,7 +70,7 @@ void LLUIImage::setScaleRegion(const LLRectf& region)
//TODO: move drawing implementation inside class
void LLUIImage::draw(S32 x, S32 y, const LLColor4& color) const
{
- gl_draw_image(x, y, mImage, color, mClipRegion);
+ gl_draw_scaled_image(x, y, getWidth(), getHeight(), mImage, color, mClipRegion);
}
void LLUIImage::draw(S32 x, S32 y, S32 width, S32 height, const LLColor4& color) const
@@ -138,22 +134,58 @@ S32 LLUIImage::getTextureHeight() const
return mImage->getHeight(0);
}
+boost::signals2::connection LLUIImage::addLoadedCallback( const image_loaded_signal_t::slot_type& cb )
+{
+ if (!mImageLoaded)
+ {
+ mImageLoaded = new image_loaded_signal_t();
+ }
+ return mImageLoaded->connect(cb);
+}
+
+
+void LLUIImage::onImageLoaded()
+{
+ if (mImageLoaded)
+ {
+ (*mImageLoaded)();
+ }
+}
+
+
namespace LLInitParam
{
- LLUIImage* TypedParam<LLUIImage*>::getValueFromBlock() const
+ void TypedParam<LLUIImage*>::setValueFromBlock() const
{
+ // The keyword "none" is specifically requesting a null image
+ // do not default to current value. Used to overwrite template images.
+ if (name() == "none")
+ {
+ mData.mValue = NULL;
+ return;
+ }
+
LLUIImage* imagep = LLUI::getUIImage(name());
- if (!imagep)
+ if (imagep)
{
- // default to current value
- imagep = mData.mValue;
+ mData.mValue = imagep;
+ }
+ }
+
+ void TypedParam<LLUIImage*>::setBlockFromValue()
+ {
+ if (mData.mValue == NULL)
+ {
+ name.set("none", false);
+ }
+ else
+ {
+ name.set(mData.mValue->getName(), false);
}
- return imagep;
}
- template<>
- bool ParamCompare<LLUIImage*>::equals(
+ bool ParamCompare<LLUIImage*, false>::equals(
LLUIImage* const &a,
LLUIImage* const &b)
{
@@ -164,3 +196,4 @@ namespace LLInitParam
return (a == b);
}
}
+
diff --git a/indra/llui/lluiimage.h b/indra/llui/lluiimage.h
index e35026cd3d..38107c112d 100644
--- a/indra/llui/lluiimage.h
+++ b/indra/llui/lluiimage.h
@@ -2,57 +2,56 @@
* @file lluiimage.h
* @brief wrapper for images used in the UI that handles smart scaling, etc.
*
- * $LicenseInfo:firstyear=2007&license=viewergpl$
- *
- * Copyright (c) 2007-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LL_LLUIIMAGE_H
#define LL_LLUIIMAGE_H
-//#include "llgl.h"
-#include "llimagegl.h"
+#include "v4color.h"
+#include "llpointer.h"
+#include "llrefcount.h"
#include "llrefcount.h"
#include "llrect.h"
#include <boost/function.hpp>
+#include <boost/signals2.hpp>
#include "llinitparam.h"
+#include "lltexture.h"
extern const LLColor4 UI_VERTEX_COLOR;
class LLUIImage : public LLRefCount
{
public:
- LLUIImage(const std::string& name, LLPointer<LLImageGL> image);
+ typedef boost::signals2::signal<void (void)> image_loaded_signal_t;
+
+ LLUIImage(const std::string& name, LLPointer<LLTexture> image);
virtual ~LLUIImage();
void setClipRegion(const LLRectf& region);
void setScaleRegion(const LLRectf& region);
- LLPointer<LLImageGL> getImage() { return mImage; }
- const LLPointer<LLImageGL>& getImage() const { return mImage; }
+ LLPointer<LLTexture> getImage() { return mImage; }
+ const LLPointer<LLTexture>& getImage() const { return mImage; }
void draw(S32 x, S32 y, S32 width, S32 height, const LLColor4& color = UI_VERTEX_COLOR) const;
void draw(S32 x, S32 y, const LLColor4& color = UI_VERTEX_COLOR) const;
@@ -60,11 +59,11 @@ public:
void drawSolid(S32 x, S32 y, S32 width, S32 height, const LLColor4& color) const;
void drawSolid(const LLRect& rect, const LLColor4& color) const { drawSolid(rect.mLeft, rect.mBottom, rect.getWidth(), rect.getHeight(), color); }
- void drawSolid(S32 x, S32 y, const LLColor4& color) const { drawSolid(x, y, mImage->getWidth(0), mImage->getHeight(0), color); }
+ void drawSolid(S32 x, S32 y, const LLColor4& color) const { drawSolid(x, y, getWidth(), getHeight(), color); }
void drawBorder(S32 x, S32 y, S32 width, S32 height, const LLColor4& color, S32 border_width) const;
void drawBorder(const LLRect& rect, const LLColor4& color, S32 border_width) const { drawBorder(rect.mLeft, rect.mBottom, rect.getWidth(), rect.getHeight(), color, border_width); }
- void drawBorder(S32 x, S32 y, const LLColor4& color, S32 border_width) const { drawBorder(x, y, mImage->getWidth(0), mImage->getHeight(0), color, border_width); }
+ void drawBorder(S32 x, S32 y, const LLColor4& color, S32 border_width) const { drawBorder(x, y, getWidth(), getHeight(), color, border_width); }
const std::string& getName() const { return mName; }
@@ -75,11 +74,17 @@ public:
S32 getTextureWidth() const;
S32 getTextureHeight() const;
+ boost::signals2::connection addLoadedCallback( const image_loaded_signal_t::slot_type& cb );
+
+ void onImageLoaded();
+
protected:
+ image_loaded_signal_t* mImageLoaded;
+
std::string mName;
LLRectf mScaleRegion;
LLRectf mClipRegion;
- LLPointer<LLImageGL> mImage;
+ LLPointer<LLTexture> mImage;
BOOL mUniformScaling;
BOOL mNoClip;
};
@@ -95,19 +100,23 @@ namespace LLInitParam
public:
Optional<std::string> name;
- TypedParam(BlockDescriptor& descriptor, const char* name, super_t::value_assignment_t value, ParamDescriptor::validation_func_t func)
- : super_t(descriptor, name, value, func)
+ 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();
}
- LLUIImage* getValueFromBlock() const;
+ void setValueFromBlock() const;
+ void setBlockFromValue();
};
// Need custom comparison function for our test app, which only loads
// LLUIImage* as NULL.
template<>
- bool ParamCompare<LLUIImage*>::equals(
- LLUIImage* const &a, LLUIImage* const &b);
+ struct ParamCompare<LLUIImage*, false>
+ {
+ static bool equals(LLUIImage* const &a, LLUIImage* const &b);
+ };
}
typedef LLPointer<LLUIImage> LLUIImagePtr;
diff --git a/indra/llui/lluistring.cpp b/indra/llui/lluistring.cpp
index 7ce0fd7a88..3e9b956ee6 100644
--- a/indra/llui/lluistring.cpp
+++ b/indra/llui/lluistring.cpp
@@ -2,31 +2,25 @@
* @file lluistring.cpp
* @brief LLUIString implementation.
*
- * $LicenseInfo:firstyear=2006&license=viewergpl$
- *
- * Copyright (c) 2006-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2006&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -35,30 +29,33 @@
#include "llsd.h"
#include "lltrans.h"
-const LLStringUtil::format_map_t LLUIString::sNullArgs;
+LLFastTimer::DeclareTimer FTM_UI_STRING("UI String");
LLUIString::LLUIString(const std::string& instring, const LLStringUtil::format_map_t& args)
- : mOrig(instring),
- mArgs(args)
+: mOrig(instring),
+ mArgs(args)
{
- format();
+ dirty();
}
void LLUIString::assign(const std::string& s)
{
mOrig = s;
- format();
+ dirty();
}
void LLUIString::setArgList(const LLStringUtil::format_map_t& args)
+
{
mArgs = args;
- format();
+ dirty();
}
void LLUIString::setArgs(const LLSD& sd)
{
+ LLFastTimer timer(FTM_UI_STRING);
+
if (!sd.isMap()) return;
for(LLSD::map_const_iterator sd_it = sd.beginMap();
sd_it != sd.endMap();
@@ -66,40 +63,40 @@ void LLUIString::setArgs(const LLSD& sd)
{
setArg(sd_it->first, sd_it->second.asString());
}
- format();
+ dirty();
}
void LLUIString::setArg(const std::string& key, const std::string& replacement)
{
mArgs[key] = replacement;
- format();
+ dirty();
}
void LLUIString::truncate(S32 maxchars)
{
- if (mWResult.size() > (size_t)maxchars)
+ if (getUpdatedWResult().size() > (size_t)maxchars)
{
- LLWStringUtil::truncate(mWResult, maxchars);
- mResult = wstring_to_utf8str(mWResult);
+ LLWStringUtil::truncate(getUpdatedWResult(), maxchars);
+ mResult = wstring_to_utf8str(getUpdatedWResult());
}
}
void LLUIString::erase(S32 charidx, S32 len)
{
- mWResult.erase(charidx, len);
- mResult = wstring_to_utf8str(mWResult);
+ getUpdatedWResult().erase(charidx, len);
+ mResult = wstring_to_utf8str(getUpdatedWResult());
}
void LLUIString::insert(S32 charidx, const LLWString& wchars)
{
- mWResult.insert(charidx, wchars);
- mResult = wstring_to_utf8str(mWResult);
+ getUpdatedWResult().insert(charidx, wchars);
+ mResult = wstring_to_utf8str(getUpdatedWResult());
}
void LLUIString::replace(S32 charidx, llwchar wc)
{
- mWResult[charidx] = wc;
- mResult = wstring_to_utf8str(mWResult);
+ getUpdatedWResult()[charidx] = wc;
+ mResult = wstring_to_utf8str(getUpdatedWResult());
}
void LLUIString::clear()
@@ -110,8 +107,18 @@ void LLUIString::clear()
mWResult.clear();
}
-void LLUIString::format()
+void LLUIString::dirty()
{
+ mNeedsResult = true;
+ mNeedsWResult = true;
+}
+
+void LLUIString::updateResult() const
+{
+ mNeedsResult = false;
+
+ LLFastTimer timer(FTM_UI_STRING);
+
// optimize for empty strings (don't attempt string replacement)
if (mOrig.empty())
{
@@ -122,8 +129,21 @@ void LLUIString::format()
mResult = mOrig;
// get the defailt args + local args
- LLStringUtil::format_map_t combined_args = LLTrans::getDefaultArgs();
- combined_args.insert(mArgs.begin(), mArgs.end());
- LLStringUtil::format(mResult, combined_args);
- mWResult = utf8str_to_wstring(mResult);
+ if (mArgs.empty())
+ {
+ LLStringUtil::format(mResult, LLTrans::getDefaultArgs());
+ }
+ else
+ {
+ LLStringUtil::format_map_t combined_args = LLTrans::getDefaultArgs();
+ combined_args.insert(mArgs.begin(), mArgs.end());
+ LLStringUtil::format(mResult, combined_args);
+ }
+}
+
+void LLUIString::updateWResult() const
+{
+ mNeedsWResult = false;
+
+ mWResult = utf8str_to_wstring(getUpdatedResult());
}
diff --git a/indra/llui/lluistring.h b/indra/llui/lluistring.h
index aedeca27cb..fc7ac37d99 100644
--- a/indra/llui/lluistring.h
+++ b/indra/llui/lluistring.h
@@ -3,31 +3,25 @@
* @author: Steve Bennetts
* @brief A fancy wrapper for std::string supporting argument substitutions.
*
- * $LicenseInfo:firstyear=2006&license=viewergpl$
- *
- * Copyright (c) 2006-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2006&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -51,9 +45,9 @@
// llinfos << mMessage.getString() << llendl; // outputs "Welcome Steve to Second Life"
// mMessage.setArg("[USERNAME]", "Joe");
// llinfos << mMessage.getString() << llendl; // outputs "Welcome Joe to Second Life"
-// mMessage = "Recepci￳n a la [SECONDLIFE] [USERNAME]"
+// mMessage = "Bienvenido a la [SECONDLIFE] [USERNAME]"
// mMessage.setArg("[SECONDLIFE]", "Segunda Vida");
-// llinfos << mMessage.getString() << llendl; // outputs "Recepci￳n a la Segunda Vida Joe"
+// llinfos << mMessage.getString() << llendl; // outputs "Bienvenido a la Segunda Vida Joe"
// Implementation Notes:
// Attempting to have operator[](const std::string& s) return mArgs[s] fails because we have
@@ -64,7 +58,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); }
@@ -76,34 +70,44 @@ public:
void setArgs(const class LLSD& sd);
void setArg(const std::string& key, const std::string& replacement);
- const std::string& getString() const { return mResult; }
- operator std::string() const { return mResult; }
+ const std::string& getString() const { return getUpdatedResult(); }
+ operator std::string() const { return getUpdatedResult(); }
- const LLWString& getWString() const { return mWResult; }
- operator LLWString() const { return mWResult; }
+ const LLWString& getWString() const { return getUpdatedWResult(); }
+ operator LLWString() const { return getUpdatedWResult(); }
- bool empty() const { return mWResult.empty(); }
- S32 length() const { return mWResult.size(); }
+ bool empty() const { return getUpdatedResult().empty(); }
+ S32 length() const { return getUpdatedWResult().size(); }
void clear();
void clearArgs() { mArgs.clear(); }
- // These utuilty functions are included for text editing.
+ // These utility functions are included for text editing.
// They do not affect mOrig and do not perform argument substitution
void truncate(S32 maxchars);
void erase(S32 charidx, S32 len);
void insert(S32 charidx, const LLWString& wchars);
void replace(S32 charidx, llwchar wc);
- static const LLStringUtil::format_map_t sNullArgs;
-
private:
- void format();
+ // something changed, requiring reformatting of strings
+ void dirty();
+
+ std::string& getUpdatedResult() const { if (mNeedsResult) { updateResult(); } return mResult; }
+ LLWString& getUpdatedWResult() const{ if (mNeedsWResult) { updateWResult(); } return mWResult; }
+
+ // do actual work of updating strings (non-inlined)
+ void updateResult() const;
+ void updateWResult() const;
std::string mOrig;
- std::string mResult;
- LLWString mWResult; // for displaying
+ mutable std::string mResult;
+ mutable LLWString mWResult; // for displaying
LLStringUtil::format_map_t mArgs;
+
+ // controls lazy evaluation
+ mutable bool mNeedsResult;
+ mutable bool mNeedsWResult;
};
#endif // LL_LLUISTRING_H
diff --git a/indra/llui/llundo.cpp b/indra/llui/llundo.cpp
index 8f57636a52..06b0514223 100644
--- a/indra/llui/llundo.cpp
+++ b/indra/llui/llundo.cpp
@@ -1,31 +1,25 @@
/**
* @file llundo.cpp
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llui/llundo.h b/indra/llui/llundo.h
index b2edb57b8f..a6da550126 100644
--- a/indra/llui/llundo.h
+++ b/indra/llui/llundo.h
@@ -2,31 +2,25 @@
* @file llundo.h
* @brief Generic interface for undo/redo circular buffer.
*
- * $LicenseInfo:firstyear=2000&license=viewergpl$
- *
- * Copyright (c) 2000-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2000&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llui/llurlaction.cpp b/indra/llui/llurlaction.cpp
new file mode 100644
index 0000000000..42b779bd28
--- /dev/null
+++ b/indra/llui/llurlaction.cpp
@@ -0,0 +1,159 @@
+/**
+ * @file llurlaction.cpp
+ * @author Martin Reddy
+ * @brief A set of actions that can performed on Urls
+ *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llurlaction.h"
+#include "llview.h"
+#include "llwindow.h"
+#include "llurlregistry.h"
+
+// global state for the callback functions
+void (*LLUrlAction::sOpenURLCallback) (const std::string& url) = NULL;
+void (*LLUrlAction::sOpenURLInternalCallback) (const std::string& url) = NULL;
+void (*LLUrlAction::sOpenURLExternalCallback) (const std::string& url) = NULL;
+bool (*LLUrlAction::sExecuteSLURLCallback) (const std::string& url) = NULL;
+
+
+void LLUrlAction::setOpenURLCallback(void (*cb) (const std::string& url))
+{
+ sOpenURLCallback = cb;
+}
+
+void LLUrlAction::setOpenURLInternalCallback(void (*cb) (const std::string& url))
+{
+ sOpenURLInternalCallback = cb;
+}
+
+void LLUrlAction::setOpenURLExternalCallback(void (*cb) (const std::string& url))
+{
+ sOpenURLExternalCallback = cb;
+}
+
+void LLUrlAction::setExecuteSLURLCallback(bool (*cb) (const std::string& url))
+{
+ sExecuteSLURLCallback = cb;
+}
+
+void LLUrlAction::openURL(std::string url)
+{
+ if (sOpenURLCallback)
+ {
+ (*sOpenURLCallback)(url);
+ }
+}
+
+void LLUrlAction::openURLInternal(std::string url)
+{
+ if (sOpenURLInternalCallback)
+ {
+ (*sOpenURLInternalCallback)(url);
+ }
+}
+
+void LLUrlAction::openURLExternal(std::string url)
+{
+ if (sOpenURLExternalCallback)
+ {
+ (*sOpenURLExternalCallback)(url);
+ }
+}
+
+void LLUrlAction::executeSLURL(std::string url)
+{
+ if (sExecuteSLURLCallback)
+ {
+ (*sExecuteSLURLCallback)(url);
+ }
+}
+
+void LLUrlAction::clickAction(std::string url)
+{
+ // Try to handle as SLURL first, then http Url
+ if ( (sExecuteSLURLCallback) && !(*sExecuteSLURLCallback)(url) )
+ {
+ if (sOpenURLCallback)
+ {
+ (*sOpenURLCallback)(url);
+ }
+ }
+}
+
+void LLUrlAction::teleportToLocation(std::string url)
+{
+ LLUrlMatch match;
+ if (LLUrlRegistry::instance().findUrl(url, match))
+ {
+ if (! match.getLocation().empty())
+ {
+ executeSLURL("secondlife:///app/teleport/" + match.getLocation());
+ }
+ }
+}
+
+void LLUrlAction::showLocationOnMap(std::string url)
+{
+ LLUrlMatch match;
+ if (LLUrlRegistry::instance().findUrl(url, match))
+ {
+ if (! match.getLocation().empty())
+ {
+ executeSLURL("secondlife:///app/worldmap/" + match.getLocation());
+ }
+ }
+}
+
+void LLUrlAction::copyURLToClipboard(std::string url)
+{
+ LLView::getWindow()->copyTextToClipboard(utf8str_to_wstring(url));
+}
+
+void LLUrlAction::copyLabelToClipboard(std::string url)
+{
+ LLUrlMatch match;
+ if (LLUrlRegistry::instance().findUrl(url, match))
+ {
+ 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
new file mode 100644
index 0000000000..0132dbaaf0
--- /dev/null
+++ b/indra/llui/llurlaction.h
@@ -0,0 +1,93 @@
+/**
+ * @file llurlaction.h
+ * @author Martin Reddy
+ * @brief A set of actions that can performed on Urls
+ *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLURLACTION_H
+#define LL_LLURLACTION_H
+
+#include <string>
+
+///
+/// The LLUrlAction class provides a number of static functions that
+/// let you open Urls in web browsers, execute SLURLs, and copy Urls
+/// to the clipboard. Many of these functions are not available at
+/// the llui level, and must be supplied via a set of callbacks.
+///
+/// N.B. The action functions specifically do not use const ref
+/// strings so that a url parameter can be used into a boost::bind()
+/// call under situations when that input string is deallocated before
+/// the callback is executed.
+///
+class LLUrlAction
+{
+public:
+ LLUrlAction();
+
+ /// load a Url in the user's preferred web browser
+ static void openURL(std::string url);
+
+ /// load a Url in the internal Second Life web browser
+ static void openURLInternal(std::string url);
+
+ /// load a Url in the operating system's default web browser
+ static void openURLExternal(std::string url);
+
+ /// execute the given secondlife: SLURL
+ static void executeSLURL(std::string url);
+
+ /// if the Url specifies an SL location, teleport there
+ static void teleportToLocation(std::string url);
+
+ /// if the Url specifies an SL location, show it on a map
+ static void showLocationOnMap(std::string url);
+
+ /// perform the appropriate action for left-clicking on a Url
+ static void clickAction(std::string url);
+
+ /// copy the label for a Url to the clipboard
+ static void copyLabelToClipboard(std::string url);
+
+ /// 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));
+ static void setOpenURLExternalCallback(void (*cb) (const std::string& url));
+ static void setExecuteSLURLCallback(bool (*cb) (const std::string& url));
+
+private:
+ // callbacks for operations we can perform on Urls
+ static void (*sOpenURLCallback) (const std::string& url);
+ static void (*sOpenURLInternalCallback) (const std::string& url);
+ static void (*sOpenURLExternalCallback) (const std::string& url);
+ static bool (*sExecuteSLURLCallback) (const std::string& url);
+};
+
+#endif
diff --git a/indra/llui/llurlentry.cpp b/indra/llui/llurlentry.cpp
new file mode 100644
index 0000000000..5680ab8bd4
--- /dev/null
+++ b/indra/llui/llurlentry.cpp
@@ -0,0 +1,838 @@
+/**
+ * @file llurlentry.cpp
+ * @author Martin Reddy
+ * @brief Describes the Url types that can be registered in LLUrlRegistry
+ *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+#include "llurlentry.h"
+#include "lluri.h"
+#include "llurlmatch.h"
+#include "llurlregistry.h"
+
+#include "llcachename.h"
+#include "lltrans.h"
+#include "lluicolortable.h"
+
+#define APP_HEADER_REGEX "((x-grid-location-info://[-\\w\\.]+/app)|(secondlife:///app))"
+
+
+LLUrlEntryBase::LLUrlEntryBase() :
+ mColor(LLUIColorTable::instance().getColor("HTMLLinkColor")),
+ mDisabledLink(false)
+{
+}
+
+LLUrlEntryBase::~LLUrlEntryBase()
+{
+}
+
+std::string LLUrlEntryBase::getUrl(const std::string &string) const
+{
+ return escapeUrl(string);
+}
+
+std::string LLUrlEntryBase::getIDStringFromUrl(const std::string &url) const
+{
+ // return the id from a SLURL in the format /app/{cmd}/{id}/about
+ LLURI uri(url);
+ LLSD path_array = uri.pathArray();
+ if (path_array.size() == 4)
+ {
+ return path_array.get(2).asString();
+ }
+ return "";
+}
+
+std::string LLUrlEntryBase::unescapeUrl(const std::string &url) const
+{
+ return LLURI::unescape(url);
+}
+
+std::string LLUrlEntryBase::escapeUrl(const std::string &url) const
+{
+ static std::string no_escape_chars;
+ static bool initialized = false;
+ if (!initialized)
+ {
+ no_escape_chars =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789"
+ "-._~!$?&()*+,@:;=/%#";
+
+ std::sort(no_escape_chars.begin(), no_escape_chars.end());
+ initialized = true;
+ }
+ return LLURI::escape(url, no_escape_chars, true);
+}
+
+std::string LLUrlEntryBase::getLabelFromWikiLink(const std::string &url) const
+{
+ // return the label part from [http://www.example.org Label]
+ const char *text = url.c_str();
+ S32 start = 0;
+ while (! isspace(text[start]))
+ {
+ start++;
+ }
+ while (text[start] == ' ' || text[start] == '\t')
+ {
+ start++;
+ }
+ return unescapeUrl(url.substr(start, url.size()-start-1));
+}
+
+std::string LLUrlEntryBase::getUrlFromWikiLink(const std::string &string) const
+{
+ // return the url part from [http://www.example.org Label]
+ const char *text = string.c_str();
+ S32 end = 0;
+ while (! isspace(text[end]))
+ {
+ end++;
+ }
+ return escapeUrl(string.substr(1, end-1));
+}
+
+void LLUrlEntryBase::addObserver(const std::string &id,
+ const std::string &url,
+ const LLUrlLabelCallback &cb)
+{
+ // add a callback to be notified when we have a label for the uuid
+ LLUrlEntryObserver observer;
+ observer.url = url;
+ observer.signal = new LLUrlLabelSignal();
+ if (observer.signal)
+ {
+ observer.signal->connect(cb);
+ mObservers.insert(std::pair<std::string, LLUrlEntryObserver>(id, observer));
+ }
+}
+
+void LLUrlEntryBase::callObservers(const std::string &id, const std::string &label)
+{
+ // notify all callbacks waiting on the given uuid
+ std::multimap<std::string, LLUrlEntryObserver>::iterator it;
+ for (it = mObservers.find(id); it != mObservers.end();)
+ {
+ // call the callback - give it the new label
+ LLUrlEntryObserver &observer = it->second;
+ (*observer.signal)(it->second.url, label);
+ // then remove the signal - we only need to call it once
+ delete observer.signal;
+ mObservers.erase(it++);
+ }
+}
+
+static std::string getStringAfterToken(const std::string str, const std::string token)
+{
+ size_t pos = str.find(token);
+ if (pos == std::string::npos)
+ {
+ return "";
+ }
+
+ pos += token.size();
+ return str.substr(pos, str.size() - pos);
+}
+
+//
+// LLUrlEntryHTTP Describes generic http: and https: Urls
+//
+LLUrlEntryHTTP::LLUrlEntryHTTP()
+{
+ mPattern = boost::regex("https?://([-\\w\\.]+)+(:\\d+)?(:\\w+)?(@\\d+)?(@\\w+)?/?\\S*",
+ boost::regex::perl|boost::regex::icase);
+ mMenuName = "menu_url_http.xml";
+ mTooltip = LLTrans::getString("TooltipHttpUrl");
+}
+
+std::string LLUrlEntryHTTP::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+{
+ return unescapeUrl(url);
+}
+
+//
+// LLUrlEntryHTTP Describes generic http: and https: Urls with custom label
+// We use the wikipedia syntax of [http://www.example.org Text]
+//
+LLUrlEntryHTTPLabel::LLUrlEntryHTTPLabel()
+{
+ mPattern = boost::regex("\\[https?://\\S+[ \t]+[^\\]]+\\]",
+ boost::regex::perl|boost::regex::icase);
+ mMenuName = "menu_url_http.xml";
+ mTooltip = LLTrans::getString("TooltipHttpUrl");
+}
+
+std::string LLUrlEntryHTTPLabel::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+{
+ return getLabelFromWikiLink(url);
+}
+
+std::string LLUrlEntryHTTPLabel::getUrl(const std::string &string) const
+{
+ return getUrlFromWikiLink(string);
+}
+
+//
+// LLUrlEntryHTTPNoProtocol Describes generic Urls like www.google.com
+//
+LLUrlEntryHTTPNoProtocol::LLUrlEntryHTTPNoProtocol()
+{
+ mPattern = boost::regex("("
+ "\\bwww\\.\\S+\\.\\S+" // i.e. www.FOO.BAR
+ "|" // or
+ "(?<!@)\\b[^[:space:]:@/>]+\\.(?:com|net|edu|org)([/:][^[:space:]<]*)?\\b" // i.e. FOO.net
+ ")",
+ boost::regex::perl|boost::regex::icase);
+ mMenuName = "menu_url_http.xml";
+ mTooltip = LLTrans::getString("TooltipHttpUrl");
+}
+
+std::string LLUrlEntryHTTPNoProtocol::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+{
+ return unescapeUrl(url);
+}
+
+std::string LLUrlEntryHTTPNoProtocol::getUrl(const std::string &string) const
+{
+ if (string.find("://") == std::string::npos)
+ {
+ return "http://" + escapeUrl(string);
+ }
+ return escapeUrl(string);
+}
+
+//
+// LLUrlEntrySLURL Describes generic http: and https: Urls
+//
+LLUrlEntrySLURL::LLUrlEntrySLURL()
+{
+ // see http://slurl.com/about.php for details on the SLURL format
+ 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");
+}
+
+std::string LLUrlEntrySLURL::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+{
+ //
+ // we handle SLURLs in the following formats:
+ // - http://slurl.com/secondlife/Place/X/Y/Z
+ // - http://slurl.com/secondlife/Place/X/Y
+ // - http://slurl.com/secondlife/Place/X
+ // - http://slurl.com/secondlife/Place
+ //
+ LLURI uri(url);
+ LLSD path_array = uri.pathArray();
+ S32 path_parts = path_array.size();
+ if (path_parts == 5)
+ {
+ // handle slurl with (X,Y,Z) coordinates
+ std::string location = unescapeUrl(path_array[path_parts-4]);
+ std::string x = path_array[path_parts-3];
+ std::string y = path_array[path_parts-2];
+ std::string z = path_array[path_parts-1];
+ return location + " (" + x + "," + y + "," + z + ")";
+ }
+ else if (path_parts == 4)
+ {
+ // handle slurl with (X,Y) coordinates
+ std::string location = unescapeUrl(path_array[path_parts-3]);
+ std::string x = path_array[path_parts-2];
+ std::string y = path_array[path_parts-1];
+ return location + " (" + x + "," + y + ")";
+ }
+ else if (path_parts == 3)
+ {
+ // handle slurl with (X) coordinate
+ std::string location = unescapeUrl(path_array[path_parts-2]);
+ std::string x = path_array[path_parts-1];
+ return location + " (" + x + ")";
+ }
+ else if (path_parts == 2)
+ {
+ // handle slurl with no coordinates
+ std::string location = unescapeUrl(path_array[path_parts-1]);
+ return location;
+ }
+
+ return url;
+}
+
+std::string LLUrlEntrySLURL::getLocation(const std::string &url) const
+{
+ // return the part of the Url after slurl.com/secondlife/
+ const std::string search_string = "/secondlife";
+ size_t pos = url.find(search_string);
+ if (pos == std::string::npos)
+ {
+ return "";
+ }
+
+ pos += search_string.size() + 1;
+ return url.substr(pos, url.size() - pos);
+}
+
+//
+// LLUrlEntryAgent Describes a Second Life agent Url, e.g.,
+// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about
+// x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about
+//
+LLUrlEntryAgent::LLUrlEntryAgent()
+{
+ mPattern = boost::regex(APP_HEADER_REGEX "/agent/[\\da-f-]+/\\w+",
+ boost::regex::perl|boost::regex::icase);
+ mMenuName = "menu_url_agent.xml";
+ mIcon = "Generic_Person";
+ mColor = LLUIColorTable::instance().getColor("AgentLinkColor");
+}
+
+void LLUrlEntryAgent::onAgentNameReceived(const LLUUID& id,
+ const std::string& first,
+ const std::string& last,
+ BOOL is_group)
+{
+ // received the agent name from the server - tell our observers
+ callObservers(id.asString(), first + " " + last);
+}
+
+LLUUID LLUrlEntryAgent::getID(const std::string &string) const
+{
+ return LLUUID(getIDStringFromUrl(string));
+}
+
+std::string LLUrlEntryAgent::getTooltip(const std::string &string) const
+{
+ // return a tooltip corresponding to the URL type instead of the generic one
+ std::string url = getUrl(string);
+
+ if (LLStringUtil::endsWith(url, "/mute"))
+ {
+ return LLTrans::getString("TooltipAgentMute");
+ }
+ if (LLStringUtil::endsWith(url, "/unmute"))
+ {
+ return LLTrans::getString("TooltipAgentUnmute");
+ }
+ if (LLStringUtil::endsWith(url, "/im"))
+ {
+ return LLTrans::getString("TooltipAgentIM");
+ }
+ if (LLStringUtil::endsWith(url, "/pay"))
+ {
+ return LLTrans::getString("TooltipAgentPay");
+ }
+ if (LLStringUtil::endsWith(url, "/offerteleport"))
+ {
+ return LLTrans::getString("TooltipAgentOfferTeleport");
+ }
+ if (LLStringUtil::endsWith(url, "/requestfriend"))
+ {
+ return LLTrans::getString("TooltipAgentRequestFriend");
+ }
+ return LLTrans::getString("TooltipAgentUrl");
+}
+
+bool LLUrlEntryAgent::underlineOnHoverOnly(const std::string &string) const
+{
+ std::string url = getUrl(string);
+ return LLStringUtil::endsWith(url, "/about") || LLStringUtil::endsWith(url, "/inspect");
+}
+
+std::string LLUrlEntryAgent::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+{
+ if (!gCacheName)
+ {
+ // probably at the login screen, use short string for layout
+ return LLTrans::getString("LoadingData");
+ }
+
+ std::string agent_id_string = getIDStringFromUrl(url);
+ if (agent_id_string.empty())
+ {
+ // something went wrong, just give raw url
+ return unescapeUrl(url);
+ }
+
+ LLUUID agent_id(agent_id_string);
+ std::string full_name;
+ if (agent_id.isNull())
+ {
+ return LLTrans::getString("AvatarNameNobody");
+ }
+ 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
+ {
+ gCacheName->get(agent_id, FALSE,
+ boost::bind(&LLUrlEntryAgent::onAgentNameReceived,
+ this, _1, _2, _3, _4));
+ addObserver(agent_id_string, url, cb);
+ return LLTrans::getString("LoadingData");
+ }
+}
+
+//
+// LLUrlEntryGroup Describes a Second Life group Url, e.g.,
+// secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/about
+// secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/inspect
+// x-grid-location-info://lincoln.lindenlab.com/app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/inspect
+//
+LLUrlEntryGroup::LLUrlEntryGroup()
+{
+ mPattern = boost::regex(APP_HEADER_REGEX "/group/[\\da-f-]+/\\w+",
+ boost::regex::perl|boost::regex::icase);
+ mMenuName = "menu_url_group.xml";
+ mIcon = "Generic_Group";
+ mTooltip = LLTrans::getString("TooltipGroupUrl");
+ mColor = LLUIColorTable::instance().getColor("GroupLinkColor");
+}
+
+
+
+void LLUrlEntryGroup::onGroupNameReceived(const LLUUID& id,
+ const std::string& first,
+ const std::string& last,
+ BOOL is_group)
+{
+ // received the group name from the server - tell our observers
+ callObservers(id.asString(), first);
+}
+
+LLUUID LLUrlEntryGroup::getID(const std::string &string) const
+{
+ return LLUUID(getIDStringFromUrl(string));
+}
+
+
+std::string LLUrlEntryGroup::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+{
+ if (!gCacheName)
+ {
+ // probably at login screen, give something short for layout
+ return LLTrans::getString("LoadingData");
+ }
+
+ std::string group_id_string = getIDStringFromUrl(url);
+ if (group_id_string.empty())
+ {
+ // something went wrong, give raw url
+ return unescapeUrl(url);
+ }
+
+ LLUUID group_id(group_id_string);
+ std::string group_name;
+ if (group_id.isNull())
+ {
+ return LLTrans::getString("GroupNameNone");
+ }
+ else if (gCacheName->getGroupName(group_id, group_name))
+ {
+ return group_name;
+ }
+ else
+ {
+ gCacheName->get(group_id, TRUE,
+ boost::bind(&LLUrlEntryGroup::onGroupNameReceived,
+ this, _1, _2, _3, _4));
+ addObserver(group_id_string, url, cb);
+ return LLTrans::getString("LoadingData");
+ }
+}
+
+//
+// LLUrlEntryInventory Describes a Second Life inventory Url, e.g.,
+// secondlife:///app/inventory/0e346d8b-4433-4d66-a6b0-fd37083abc4c/select
+//
+LLUrlEntryInventory::LLUrlEntryInventory()
+{
+ //*TODO: add supporting of inventory item names with whitespaces
+ //this pattern cann't parse for example
+ //secondlife:///app/inventory/0e346d8b-4433-4d66-a6b0-fd37083abc4c/select?name=name with spaces&param2=value
+ //x-grid-location-info://lincoln.lindenlab.com/app/inventory/0e346d8b-4433-4d66-a6b0-fd37083abc4c/select?name=name with spaces&param2=value
+ mPattern = boost::regex(APP_HEADER_REGEX "/inventory/[\\da-f-]+/\\w+\\S*",
+ boost::regex::perl|boost::regex::icase);
+ mMenuName = "menu_url_inventory.xml";
+}
+
+std::string LLUrlEntryInventory::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+{
+ std::string label = getStringAfterToken(url, "name=");
+ return LLURI::unescape(label.empty() ? url : label);
+}
+
+//
+// LLUrlEntryObjectIM Describes a Second Life inspector for the object Url, e.g.,
+// secondlife:///app/objectim/7bcd7864-da6b-e43f-4486-91d28a28d95b?name=Object&owner=3de548e1-57be-cfea-2b78-83ae3ad95998&slurl=Danger!%20Danger!/200/200/30/&groupowned=1
+//
+LLUrlEntryObjectIM::LLUrlEntryObjectIM()
+{
+ mPattern = boost::regex("secondlife:///app/objectim/[\\da-f-]+\?.*",
+ 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
+/// x-grid-location-info://lincoln.lindenlab.com/app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about
+///
+LLUrlEntryParcel::LLUrlEntryParcel()
+{
+ mPattern = boost::regex(APP_HEADER_REGEX "/parcel/[\\da-f-]+/about",
+ boost::regex::perl|boost::regex::icase);
+ mMenuName = "menu_url_parcel.xml";
+ mTooltip = LLTrans::getString("TooltipParcelUrl");
+}
+
+std::string LLUrlEntryParcel::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+{
+ return unescapeUrl(url);
+}
+
+//
+// LLUrlEntryPlace Describes secondlife://<location> URLs
+//
+LLUrlEntryPlace::LLUrlEntryPlace()
+{
+ mPattern = boost::regex("((x-grid-location-info://[-\\w\\.]+/region/)|(secondlife://))\\S+/?(\\d+/\\d+/\\d+|\\d+/\\d+)/?",
+ boost::regex::perl|boost::regex::icase);
+ mMenuName = "menu_url_slurl.xml";
+ mTooltip = LLTrans::getString("TooltipSLURL");
+}
+
+std::string LLUrlEntryPlace::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+{
+ //
+ // we handle SLURLs in the following formats:
+ // - secondlife://Place/X/Y/Z
+ // - secondlife://Place/X/Y
+ //
+ LLURI uri(url);
+ std::string location = unescapeUrl(uri.hostName());
+ LLSD path_array = uri.pathArray();
+ S32 path_parts = path_array.size();
+ if (path_parts == 3)
+ {
+ // handle slurl with (X,Y,Z) coordinates
+ std::string x = path_array[0];
+ std::string y = path_array[1];
+ std::string z = path_array[2];
+ return location + " (" + x + "," + y + "," + z + ")";
+ }
+ else if (path_parts == 2)
+ {
+ // handle slurl with (X,Y) coordinates
+ std::string x = path_array[0];
+ std::string y = path_array[1];
+ return location + " (" + x + "," + y + ")";
+ }
+
+ return url;
+}
+
+std::string LLUrlEntryPlace::getLocation(const std::string &url) const
+{
+ // return the part of the Url after secondlife:// part
+ return ::getStringAfterToken(url, "://");
+}
+
+//
+// LLUrlEntryTeleport Describes a Second Life teleport Url, e.g.,
+// secondlife:///app/teleport/Ahern/50/50/50/
+// x-grid-location-info://lincoln.lindenlab.com/app/teleport/Ahern/50/50/50/
+//
+LLUrlEntryTeleport::LLUrlEntryTeleport()
+{
+ mPattern = boost::regex(APP_HEADER_REGEX "/teleport/\\S+(/\\d+)?(/\\d+)?(/\\d+)?/?\\S*",
+ boost::regex::perl|boost::regex::icase);
+ mMenuName = "menu_url_teleport.xml";
+ mTooltip = LLTrans::getString("TooltipTeleportUrl");
+}
+
+std::string LLUrlEntryTeleport::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+{
+ //
+ // we handle teleport SLURLs in the following formats:
+ // - secondlife:///app/teleport/Place/X/Y/Z
+ // - secondlife:///app/teleport/Place/X/Y
+ // - secondlife:///app/teleport/Place/X
+ // - secondlife:///app/teleport/Place
+ //
+ LLURI uri(url);
+ LLSD path_array = uri.pathArray();
+ S32 path_parts = path_array.size();
+ std::string host = uri.hostName();
+ std::string label = LLTrans::getString("SLurlLabelTeleport");
+ if (!host.empty())
+ {
+ label += " " + host;
+ }
+ if (path_parts == 6)
+ {
+ // handle teleport url with (X,Y,Z) coordinates
+ std::string location = unescapeUrl(path_array[path_parts-4]);
+ std::string x = path_array[path_parts-3];
+ std::string y = path_array[path_parts-2];
+ std::string z = path_array[path_parts-1];
+ return label + " " + location + " (" + x + "," + y + "," + z + ")";
+ }
+ else if (path_parts == 5)
+ {
+ // handle teleport url with (X,Y) coordinates
+ std::string location = unescapeUrl(path_array[path_parts-3]);
+ std::string x = path_array[path_parts-2];
+ std::string y = path_array[path_parts-1];
+ return label + " " + location + " (" + x + "," + y + ")";
+ }
+ else if (path_parts == 4)
+ {
+ // handle teleport url with (X) coordinate only
+ std::string location = unescapeUrl(path_array[path_parts-2]);
+ std::string x = path_array[path_parts-1];
+ return label + " " + location + " (" + x + ")";
+ }
+ else if (path_parts == 3)
+ {
+ // handle teleport url with no coordinates
+ std::string location = unescapeUrl(path_array[path_parts-1]);
+ return label + " " + location;
+ }
+
+ return url;
+}
+
+std::string LLUrlEntryTeleport::getLocation(const std::string &url) const
+{
+ // return the part of the Url after ///app/teleport
+ return ::getStringAfterToken(url, "app/teleport/");
+}
+
+//
+// LLUrlEntrySL Describes a generic SLURL, e.g., a Url that starts
+// with secondlife:// (used as a catch-all for cases not matched above)
+//
+LLUrlEntrySL::LLUrlEntrySL()
+{
+ mPattern = boost::regex("secondlife://(\\w+)?(:\\d+)?/\\S+",
+ boost::regex::perl|boost::regex::icase);
+ mMenuName = "menu_url_slapp.xml";
+ mTooltip = LLTrans::getString("TooltipSLAPP");
+}
+
+std::string LLUrlEntrySL::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+{
+ return unescapeUrl(url);
+}
+
+//
+// LLUrlEntrySLLabel Describes a generic SLURL, e.g., a Url that starts
+/// with secondlife:// with the ability to specify a custom label.
+//
+LLUrlEntrySLLabel::LLUrlEntrySLLabel()
+{
+ mPattern = boost::regex("\\[secondlife://\\S+[ \t]+[^\\]]+\\]",
+ boost::regex::perl|boost::regex::icase);
+ mMenuName = "menu_url_slapp.xml";
+ mTooltip = LLTrans::getString("TooltipSLAPP");
+}
+
+std::string LLUrlEntrySLLabel::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+{
+ return getLabelFromWikiLink(url);
+}
+
+std::string LLUrlEntrySLLabel::getUrl(const std::string &string) const
+{
+ return getUrlFromWikiLink(string);
+}
+
+std::string LLUrlEntrySLLabel::getTooltip(const std::string &string) const
+{
+ // return a tooltip corresponding to the URL type instead of the generic one (EXT-4574)
+ std::string url = getUrl(string);
+ LLUrlMatch match;
+ if (LLUrlRegistry::instance().findUrl(url, match))
+ {
+ return match.getTooltip();
+ }
+
+ // unrecognized URL? should not happen
+ return LLUrlEntryBase::getTooltip(string);
+}
+
+bool LLUrlEntrySLLabel::underlineOnHoverOnly(const std::string &string) const
+{
+ std::string url = getUrl(string);
+ LLUrlMatch match;
+ if (LLUrlRegistry::instance().findUrl(url, match))
+ {
+ return match.underlineOnHoverOnly();
+ }
+
+ // unrecognized URL? should not happen
+ return LLUrlEntryBase::underlineOnHoverOnly(string);
+}
+
+//
+// LLUrlEntryWorldMap Describes secondlife:///<location> URLs
+//
+LLUrlEntryWorldMap::LLUrlEntryWorldMap()
+{
+ mPattern = boost::regex(APP_HEADER_REGEX "/worldmap/\\S+/?(\\d+)?/?(\\d+)?/?(\\d+)?/?\\S*",
+ boost::regex::perl|boost::regex::icase);
+ mMenuName = "menu_url_map.xml";
+ mTooltip = LLTrans::getString("TooltipMapUrl");
+}
+
+std::string LLUrlEntryWorldMap::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+{
+ //
+ // we handle SLURLs in the following formats:
+ // - secondlife:///app/worldmap/PLACE/X/Y/Z
+ // - secondlife:///app/worldmap/PLACE/X/Y
+ // - secondlife:///app/worldmap/PLACE/X
+ //
+ LLURI uri(url);
+ LLSD path_array = uri.pathArray();
+ S32 path_parts = path_array.size();
+ if (path_parts < 3)
+ {
+ return url;
+ }
+
+ const std::string label = LLTrans::getString("SLurlLabelShowOnMap");
+ std::string location = unescapeUrl(path_array[2]);
+ std::string x = (path_parts > 3) ? path_array[3] : "128";
+ std::string y = (path_parts > 4) ? path_array[4] : "128";
+ std::string z = (path_parts > 5) ? path_array[5] : "0";
+ return label + " " + location + " (" + x + "," + y + "," + z + ")";
+}
+
+std::string LLUrlEntryWorldMap::getLocation(const std::string &url) const
+{
+ // return the part of the Url after secondlife:///app/worldmap/ part
+ return ::getStringAfterToken(url, "app/worldmap/");
+}
+
+//
+// LLUrlEntryNoLink lets us turn of URL detection with <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
new file mode 100644
index 0000000000..e25eaa7555
--- /dev/null
+++ b/indra/llui/llurlentry.h
@@ -0,0 +1,315 @@
+/**
+ * @file llurlentry.h
+ * @author Martin Reddy
+ * @brief Describes the Url types that can be registered in LLUrlRegistry
+ *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLURLENTRY_H
+#define LL_LLURLENTRY_H
+
+#include "lluuid.h"
+#include "lluicolor.h"
+#include <boost/signals2.hpp>
+#include <boost/regex.hpp>
+#include <string>
+#include <map>
+
+typedef boost::signals2::signal<void (const std::string& url,
+ const std::string& label)> LLUrlLabelSignal;
+typedef LLUrlLabelSignal::slot_type LLUrlLabelCallback;
+
+///
+/// LLUrlEntryBase is the base class of all Url types registered in the
+/// LLUrlRegistry. Each derived classes provides a regular expression
+/// to match the Url type (e.g., http://... or secondlife://...) along
+/// with an optional icon to display next to instances of the Url in
+/// a text display and a XUI file to use for any context menu popup.
+/// Functions are also provided to compute an appropriate label and
+/// tooltip/status bar text for the Url.
+///
+/// Some derived classes of LLUrlEntryBase may wish to compute an
+/// appropriate label for a Url by asking the server for information.
+/// You must therefore provide a callback method, so that you can be
+/// notified when an updated label has been received from the server.
+/// This label should then be used to replace any previous label
+/// that you received from getLabel() for the Url in question.
+///
+class LLUrlEntryBase
+{
+public:
+ LLUrlEntryBase();
+ virtual ~LLUrlEntryBase();
+
+ /// Return the regex pattern that matches this Url
+ boost::regex getPattern() const { return mPattern; }
+
+ /// Return the url from a string that matched the regex
+ virtual std::string getUrl(const std::string &string) const;
+
+ /// Given a matched Url, return a label for the Url
+ virtual std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) { return url; }
+
+ /// Return an icon that can be displayed next to Urls of this type
+ 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
+ virtual std::string getTooltip(const std::string &string) const { return mTooltip; }
+
+ /// Return the name of a XUI file containing the context menu items
+ std::string getMenuName() const { return mMenuName; }
+
+ /// Return the name of a SL location described by this Url, if any
+ virtual std::string getLocation(const std::string &url) const { return ""; }
+
+ /// is this a match for a URL that should not be hyperlinked?
+ bool isLinkDisabled() const { return mDisabledLink; }
+
+ /// Should this link text be underlined only when mouse is hovered over it?
+ virtual bool underlineOnHoverOnly(const std::string &string) const { return false; }
+
+ virtual LLUUID getID(const std::string &string) const { return LLUUID::null; }
+
+protected:
+ std::string getIDStringFromUrl(const std::string &url) const;
+ std::string escapeUrl(const std::string &url) const;
+ std::string unescapeUrl(const std::string &url) const;
+ std::string getLabelFromWikiLink(const std::string &url) const;
+ std::string getUrlFromWikiLink(const std::string &string) const;
+ void addObserver(const std::string &id, const std::string &url, const LLUrlLabelCallback &cb);
+ void callObservers(const std::string &id, const std::string &label);
+
+ typedef struct {
+ std::string url;
+ LLUrlLabelSignal *signal;
+ } LLUrlEntryObserver;
+
+ boost::regex mPattern;
+ std::string mIcon;
+ std::string mMenuName;
+ std::string mTooltip;
+ LLUIColor mColor;
+ std::multimap<std::string, LLUrlEntryObserver> mObservers;
+ bool mDisabledLink;
+};
+
+///
+/// LLUrlEntryHTTP Describes generic http: and https: Urls
+///
+class LLUrlEntryHTTP : public LLUrlEntryBase
+{
+public:
+ LLUrlEntryHTTP();
+ /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
+};
+
+///
+/// LLUrlEntryHTTPLabel Describes generic http: and https: Urls with custom labels
+///
+class LLUrlEntryHTTPLabel : public LLUrlEntryBase
+{
+public:
+ LLUrlEntryHTTPLabel();
+ /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
+ /*virtual*/ std::string getUrl(const std::string &string) const;
+};
+
+///
+/// LLUrlEntryHTTPNoProtocol Describes generic Urls like www.google.com
+///
+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) const;
+};
+
+///
+/// LLUrlEntrySLURL Describes http://slurl.com/... Urls
+///
+class LLUrlEntrySLURL : public LLUrlEntryBase
+{
+public:
+ LLUrlEntrySLURL();
+ /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
+ /*virtual*/ std::string getLocation(const std::string &url) const;
+};
+
+///
+/// LLUrlEntryAgent Describes a Second Life agent Url, e.g.,
+/// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about
+///
+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;
+ /*virtual*/ LLUUID getID(const std::string &string) const;
+ /*virtual*/ bool underlineOnHoverOnly(const std::string &string) const;
+private:
+ void onAgentNameReceived(const LLUUID& id, const std::string& first,
+ const std::string& last, BOOL is_group);
+};
+
+///
+/// LLUrlEntryGroup Describes a Second Life group Url, e.g.,
+/// secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/about
+///
+class LLUrlEntryGroup : public LLUrlEntryBase
+{
+public:
+ LLUrlEntryGroup();
+ /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
+ /*virtual*/ LLUUID getID(const std::string &string) const;
+private:
+ void onGroupNameReceived(const LLUUID& id, const std::string& first,
+ const std::string& last, BOOL is_group);
+};
+
+///
+/// LLUrlEntryInventory Describes a Second Life inventory Url, e.g.,
+/// secondlife:///app/inventory/0e346d8b-4433-4d66-a6b0-fd37083abc4c/select
+///
+class LLUrlEntryInventory : public LLUrlEntryBase
+{
+public:
+ LLUrlEntryInventory();
+ /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
+private:
+};
+
+///
+/// LLUrlEntryObjectIM Describes a Second Life inspector for the object Url, e.g.,
+/// secondlife:///app/objectim/7bcd7864-da6b-e43f-4486-91d28a28d95b?name=Object&owner=3de548e1-57be-cfea-2b78-83ae3ad95998&slurl=Danger!%20Danger!/200/200/30/&groupowned=1
+///
+class LLUrlEntryObjectIM : public LLUrlEntryBase
+{
+public:
+ LLUrlEntryObjectIM();
+ /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
+ /*virtual*/ std::string getLocation(const std::string &url) const;
+private:
+};
+
+///
+/// LLUrlEntryParcel Describes a Second Life parcel Url, e.g.,
+/// secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about
+///
+class LLUrlEntryParcel : public LLUrlEntryBase
+{
+public:
+ LLUrlEntryParcel();
+ /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
+};
+
+///
+/// LLUrlEntryPlace Describes a Second Life location Url, e.g.,
+/// secondlife://Ahern/50/50/50
+///
+class LLUrlEntryPlace : public LLUrlEntryBase
+{
+public:
+ LLUrlEntryPlace();
+ /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
+ /*virtual*/ std::string getLocation(const std::string &url) const;
+};
+
+///
+/// LLUrlEntryTeleport Describes a Second Life teleport Url, e.g.,
+/// secondlife:///app/teleport/Ahern/50/50/50/
+///
+class LLUrlEntryTeleport : public LLUrlEntryBase
+{
+public:
+ LLUrlEntryTeleport();
+ /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
+ /*virtual*/ std::string getLocation(const std::string &url) const;
+};
+
+///
+/// LLUrlEntrySL Describes a generic SLURL, e.g., a Url that starts
+/// with secondlife:// (used as a catch-all for cases not matched above)
+///
+class LLUrlEntrySL : public LLUrlEntryBase
+{
+public:
+ LLUrlEntrySL();
+ /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
+};
+
+///
+/// LLUrlEntrySLLabel Describes a generic SLURL, e.g., a Url that starts
+/// with secondlife:// with the ability to specify a custom label.
+///
+class LLUrlEntrySLLabel : public LLUrlEntryBase
+{
+public:
+ LLUrlEntrySLLabel();
+ /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
+ /*virtual*/ std::string getUrl(const std::string &string) const;
+ /*virtual*/ std::string getTooltip(const std::string &string) const;
+ /*virtual*/ bool underlineOnHoverOnly(const std::string &string) const;
+};
+
+///
+/// LLUrlEntryWorldMap Describes a Second Life worldmap Url, e.g.,
+/// secondlife:///app/worldmap/Ahern/50/50/50
+///
+class LLUrlEntryWorldMap : public LLUrlEntryBase
+{
+public:
+ LLUrlEntryWorldMap();
+ /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
+ /*virtual*/ std::string getLocation(const std::string &url) const;
+};
+
+///
+/// LLUrlEntryNoLink lets us turn of URL detection with <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
new file mode 100644
index 0000000000..e53b0c4370
--- /dev/null
+++ b/indra/llui/llurlmatch.cpp
@@ -0,0 +1,63 @@
+/**
+ * @file llurlmatch.cpp
+ * @author Martin Reddy
+ * @brief Specifies a matched Url in a string, as returned by LLUrlRegistry
+ *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+#include "llurlmatch.h"
+
+LLUrlMatch::LLUrlMatch() :
+ mStart(0),
+ mEnd(0),
+ mUrl(""),
+ mLabel(""),
+ mTooltip(""),
+ mIcon(""),
+ mMenuName(""),
+ mLocation(""),
+ mDisabledLink(false),
+ mUnderlineOnHoverOnly(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,
+ bool disabled_link, const LLUUID& id, bool underline_on_hover_only)
+{
+ mStart = start;
+ mEnd = end;
+ mUrl = url;
+ mLabel = label;
+ mTooltip = tooltip;
+ mIcon = icon;
+ mColor = color;
+ mMenuName = menu;
+ mLocation = location;
+ mDisabledLink = disabled_link;
+ mID = id;
+ mUnderlineOnHoverOnly = underline_on_hover_only;
+}
diff --git a/indra/llui/llurlmatch.h b/indra/llui/llurlmatch.h
new file mode 100644
index 0000000000..d1b2112ee7
--- /dev/null
+++ b/indra/llui/llurlmatch.h
@@ -0,0 +1,111 @@
+/**
+ * @file llurlmatch.h
+ * @author Martin Reddy
+ * @brief Specifies a matched Url in a string, as returned by LLUrlRegistry
+ *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLURLMATCH_H
+#define LL_LLURLMATCH_H
+
+#include "linden_common.h"
+
+#include <string>
+#include <vector>
+#include "lluicolor.h"
+
+///
+/// LLUrlMatch describes a single Url that was matched within a string by
+/// the LLUrlRegistry::findUrl() method. It includes the actual Url that
+/// was matched along with its first/last character offset in the string.
+/// An alternate label is also provided for creating a hyperlink, as well
+/// as tooltip/status text, an icon, and a XUI file for a context menu
+/// that can be used in a popup for a Url (e.g., Open, Copy URL, etc.)
+///
+class LLUrlMatch
+{
+public:
+ LLUrlMatch();
+
+ /// return true if this object does not contain a valid Url match yet
+ bool empty() const { return mUrl.empty(); }
+
+ /// return the offset in the string for the first character of the Url
+ U32 getStart() const { return mStart; }
+
+ /// return the offset in the string for the last character of the Url
+ U32 getEnd() const { return mEnd; }
+
+ /// return the Url that has been matched in the input string
+ std::string getUrl() const { return mUrl; }
+
+ /// return a label that can be used for the display of this Url
+ std::string getLabel() const { return mLabel; }
+
+ /// return a message that could be displayed in a tooltip or status bar
+ std::string getTooltip() const { return mTooltip; }
+
+ /// return the filename for an icon that can be displayed next to this Url
+ std::string getIcon() const { return mIcon; }
+
+ /// Return the color to render the displayed text
+ LLUIColor getColor() const { return mColor; }
+
+ /// Return the name of a XUI file containing the context menu items
+ std::string getMenuName() const { return mMenuName; }
+
+ /// 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; }
+
+ /// Should this link text be underlined only when mouse is hovered over it?
+ bool underlineOnHoverOnly() const { return mUnderlineOnHoverOnly; }
+
+ /// 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, bool disabled_link
+ , const LLUUID& id, bool underline_on_hover_only = false );
+
+ const LLUUID& getID() const { return mID;}
+
+private:
+ U32 mStart;
+ U32 mEnd;
+ std::string mUrl;
+ std::string mLabel;
+ std::string mTooltip;
+ std::string mIcon;
+ std::string mMenuName;
+ std::string mLocation;
+
+ LLUUID mID;
+ LLUIColor mColor;
+ bool mDisabledLink;
+ bool mUnderlineOnHoverOnly;
+};
+
+#endif
diff --git a/indra/llui/llurlregistry.cpp b/indra/llui/llurlregistry.cpp
new file mode 100644
index 0000000000..9d215cf7ef
--- /dev/null
+++ b/indra/llui/llurlregistry.cpp
@@ -0,0 +1,254 @@
+/**
+ * @file llurlregistry.cpp
+ * @author Martin Reddy
+ * @brief Contains a set of Url types that can be matched in a string
+ *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+#include "llurlregistry.h"
+
+#include <boost/regex.hpp>
+
+// default dummy callback that ignores any label updates from the server
+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());
+ registerUrl(new LLUrlEntryAgent());
+ registerUrl(new LLUrlEntryGroup());
+ 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());
+ registerUrl(new LLUrlEntrySLLabel());
+ // most common pattern is a URL without any protocol,
+ // e.g., "secondlife.com"
+ registerUrl(new LLUrlEntryHTTPNoProtocol());
+}
+
+LLUrlRegistry::~LLUrlRegistry()
+{
+ // free all of the LLUrlEntryBase objects we are holding
+ std::vector<LLUrlEntryBase *>::iterator it;
+ for (it = mUrlEntry.begin(); it != mUrlEntry.end(); ++it)
+ {
+ delete *it;
+ }
+}
+
+void LLUrlRegistry::registerUrl(LLUrlEntryBase *url)
+{
+ if (url)
+ {
+ mUrlEntry.push_back(url);
+ }
+}
+
+static bool matchRegex(const char *text, boost::regex regex, U32 &start, U32 &end)
+{
+ boost::cmatch result;
+ bool found;
+
+ // regex_search can potentially throw an exception, so check for it
+ try
+ {
+ found = boost::regex_search(text, result, regex);
+ }
+ catch (std::runtime_error &)
+ {
+ return false;
+ }
+
+ if (! found)
+ {
+ return false;
+ }
+
+ // return the first/last character offset for the matched substring
+ start = static_cast<U32>(result[0].first - text);
+ end = static_cast<U32>(result[0].second - text) - 1;
+
+ // we allow certain punctuation to terminate a Url but not match it,
+ // e.g., "http://foo.com/." should just match "http://foo.com/"
+ if (text[end] == '.' || text[end] == ',')
+ {
+ end--;
+ }
+ // ignore a terminating ')' when Url contains no matching '('
+ // see DEV-19842 for details
+ else if (text[end] == ')' && std::string(text+start, end-start).find('(') == std::string::npos)
+ {
+ end--;
+ }
+
+ return true;
+}
+
+static bool stringHasUrl(const std::string &text)
+{
+ // fast heuristic test for a URL in a string. This is used
+ // to avoid lots of costly regex calls, BUT it needs to be
+ // kept in sync with the LLUrlEntry regexes we support.
+ return (text.find("://") != std::string::npos ||
+ text.find("www.") != std::string::npos ||
+ 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("<nolink>") != std::string::npos ||
+ text.find("<icon") != std::string::npos);
+}
+
+bool LLUrlRegistry::findUrl(const std::string &text, LLUrlMatch &match, const LLUrlLabelCallback &cb)
+{
+ // avoid costly regexes if there is clearly no URL in the text
+ if (! stringHasUrl(text))
+ {
+ return false;
+ }
+
+ // find the first matching regex from all url entries in the registry
+ U32 match_start = 0, match_end = 0;
+ LLUrlEntryBase *match_entry = NULL;
+
+ std::vector<LLUrlEntryBase *>::iterator it;
+ for (it = mUrlEntry.begin(); it != mUrlEntry.end(); ++it)
+ {
+ LLUrlEntryBase *url_entry = *it;
+
+ U32 start = 0, end = 0;
+ if (matchRegex(text.c_str(), url_entry->getPattern(), start, end))
+ {
+ // does this match occur in the string before any other match
+ if (start < match_start || match_entry == NULL)
+ {
+ match_start = start;
+ match_end = end;
+ match_entry = url_entry;
+ }
+ }
+ }
+
+ // did we find a match? if so, return its details in the match object
+ if (match_entry)
+ {
+ // fill in the LLUrlMatch object and return it
+ std::string url = text.substr(match_start, match_end - match_start + 1);
+ match.setValues(match_start, match_end,
+ match_entry->getUrl(url),
+ match_entry->getLabel(url, cb),
+ match_entry->getTooltip(url),
+ match_entry->getIcon(url),
+ match_entry->getColor(),
+ match_entry->getMenuName(),
+ match_entry->getLocation(url),
+ match_entry->isLinkDisabled(),
+ match_entry->getID(url),
+ match_entry->underlineOnHoverOnly(url));
+ return true;
+ }
+
+ return false;
+}
+
+bool LLUrlRegistry::findUrl(const LLWString &text, LLUrlMatch &match, const LLUrlLabelCallback &cb)
+{
+ // boost::regex_search() only works on char or wchar_t
+ // types, but wchar_t is only 2-bytes on Win32 (not 4).
+ // So we use UTF-8 to make this work the same everywhere.
+ std::string utf8_text = wstring_to_utf8str(text);
+ if (findUrl(utf8_text, match, cb))
+ {
+ // we cannot blindly return the start/end offsets from
+ // the UTF-8 string because it is a variable-length
+ // character encoding, so we need to update the start
+ // and end values to be correct for the wide string.
+ LLWString wurl = utf8str_to_wstring(match.getUrl());
+ S32 start = text.find(wurl);
+ if (start == std::string::npos)
+ {
+ return false;
+ }
+ 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.isLinkDisabled(),
+ match.getID(),
+ match.underlineOnHoverOnly());
+ return true;
+ }
+ return false;
+}
+
+bool LLUrlRegistry::hasUrl(const std::string &text)
+{
+ LLUrlMatch match;
+ return findUrl(text, match);
+}
+
+bool LLUrlRegistry::hasUrl(const LLWString &text)
+{
+ LLUrlMatch match;
+ return findUrl(text, match);
+}
+
+bool LLUrlRegistry::isUrl(const std::string &text)
+{
+ LLUrlMatch match;
+ if (findUrl(text, match))
+ {
+ return (match.getStart() == 0 && match.getEnd() >= text.size()-1);
+ }
+ return false;
+}
+
+bool LLUrlRegistry::isUrl(const LLWString &text)
+{
+ LLUrlMatch match;
+ if (findUrl(text, match))
+ {
+ return (match.getStart() == 0 && match.getEnd() >= text.size()-1);
+ }
+ return false;
+}
diff --git a/indra/llui/llurlregistry.h b/indra/llui/llurlregistry.h
new file mode 100644
index 0000000000..24ce516c43
--- /dev/null
+++ b/indra/llui/llurlregistry.h
@@ -0,0 +1,93 @@
+/**
+ * @file llurlregistry.h
+ * @author Martin Reddy
+ * @brief Contains a set of Url types that can be matched in a string
+ *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLURLREGISTRY_H
+#define LL_LLURLREGISTRY_H
+
+#include "llurlentry.h"
+#include "llurlmatch.h"
+#include "llsingleton.h"
+#include "llstring.h"
+
+#include <string>
+#include <vector>
+
+/// This default callback for findUrl() simply ignores any label updates
+void LLUrlRegistryNullCallback(const std::string &url, const std::string &label);
+
+///
+/// LLUrlRegistry is a singleton that contains a set of Url types that
+/// can be matched in string. E.g., http:// or secondlife:// Urls.
+///
+/// Clients call the findUrl() method on a string to locate the first
+/// occurence of a supported Urls in that string. If findUrl() returns
+/// true, the LLUrlMatch object will be updated to describe the Url
+/// that was matched, including a label that can be used to hyperlink
+/// the Url, an icon to display next to the Url, and a XUI menu that
+/// can be used as a popup context menu for that Url.
+///
+/// New Url types can be added to the registry with the registerUrl
+/// method. E.g., to add support for a new secondlife:///app/ Url.
+///
+/// Computing the label for a Url could involve a roundtrip request
+/// to the server (e.g., to find the actual agent or group name).
+/// As such, you can provide a callback method that will get invoked
+/// when a new label is available for one of your matched Urls.
+///
+class LLUrlRegistry : public LLSingleton<LLUrlRegistry>
+{
+public:
+ ~LLUrlRegistry();
+
+ /// add a new Url handler to the registry (will be freed on destruction)
+ void registerUrl(LLUrlEntryBase *url);
+
+ /// get the next Url in an input string, starting at a given character offset
+ /// your callback is invoked if the matched Url's label changes in the future
+ bool findUrl(const std::string &text, LLUrlMatch &match,
+ const LLUrlLabelCallback &cb = &LLUrlRegistryNullCallback);
+
+ /// a slightly less efficient version of findUrl for wide strings
+ bool findUrl(const LLWString &text, LLUrlMatch &match,
+ const LLUrlLabelCallback &cb = &LLUrlRegistryNullCallback);
+
+ // return true if the given string contains a URL that findUrl would match
+ bool hasUrl(const std::string &text);
+ bool hasUrl(const LLWString &text);
+
+ // return true if the given string is a URL that findUrl would match
+ bool isUrl(const std::string &text);
+ bool isUrl(const LLWString &text);
+
+private:
+ LLUrlRegistry();
+ friend class LLSingleton<LLUrlRegistry>;
+
+ std::vector<LLUrlEntryBase *> mUrlEntry;
+};
+
+#endif
diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp
index 2e2ef4d79f..fe5ef269a9 100644
--- a/indra/llui/llview.cpp
+++ b/indra/llui/llview.cpp
@@ -3,36 +3,31 @@
* @author James Cook
* @brief Container for other views, anything that draws.
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "linden_common.h"
+#define LLVIEW_CPP
#include "llview.h"
#include <cassert>
@@ -40,7 +35,6 @@
#include "llrender.h"
#include "llevent.h"
-#include "llfontgl.h"
#include "llfocusmgr.h"
#include "llrect.h"
#include "llstl.h"
@@ -49,6 +43,7 @@
#include "llwindow.h"
#include "v3color.h"
#include "lluictrlfactory.h"
+#include "lltooltip.h"
// for ui edit hack
#include "llbutton.h"
@@ -56,24 +51,36 @@
#include "lltexteditor.h"
#include "lltextbox.h"
-BOOL LLView::sDebugRects = FALSE;
-BOOL LLView::sDebugKeys = FALSE;
S32 LLView::sDepth = 0;
-BOOL LLView::sDebugMouseHandling = FALSE;
+bool LLView::sDebugRects = false;
+bool LLView::sDebugRectsShowNames = true;
+bool LLView::sDebugKeys = false;
+bool LLView::sDebugMouseHandling = false;
std::string LLView::sMouseHandlerMessage;
-//BOOL LLView::sEditingUI = FALSE;
BOOL LLView::sForceReshape = FALSE;
-//LLView* LLView::sEditingUIView = NULL;
std::set<LLView*> LLView::sPreviewHighlightedElements;
BOOL LLView::sHighlightingDiffs = FALSE;
LLView* LLView::sPreviewClickedElement = NULL;
BOOL LLView::sDrawPreviewHighlights = FALSE;
S32 LLView::sLastLeftXML = S32_MIN;
S32 LLView::sLastBottomXML = S32_MIN;
+std::vector<LLViewDrawContext*> 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>(
+ const std::string& name, BOOL recurse) const;
+
+static LLDefaultChildRegistry::Register<LLView> r("view");
+
+LLView::Follows::Follows()
+: string(""),
+ flags("flags", FOLLOWS_LEFT | FOLLOWS_TOP)
+{}
LLView::Params::Params()
: name("name", std::string("unnamed")),
@@ -87,9 +94,6 @@ LLView::Params::Params()
default_tab_group("default_tab_group"),
tool_tip("tool_tip"),
sound_flags("sound_flags", MOUSE_UP),
- font("font", LLFontGL::getFontSansSerif()),
- font_halign("halign"),
- font_valign("valign"),
layout("layout"),
rect("rect"),
bottom_delta("bottom_delta", S32_MAX),
@@ -97,12 +101,17 @@ LLView::Params::Params()
top_delta("top_delta", S32_MAX),
left_pad("left_pad"),
left_delta("left_delta", S32_MAX),
- center_horiz("center_horiz", false),
- center_vert("center_vert", false),
- serializable("", false),
+ from_xui("from_xui", false),
user_resize("user_resize"),
auto_resize("auto_resize"),
- needs_translate("translate")
+ needs_translate("translate"),
+ min_width("min_width"),
+ max_width("max_width"),
+ xmlns("xmlns"),
+ xmlns_xsi("xmlns:xsi"),
+ xsi_schemaLocation("xsi:schemaLocation"),
+ xsi_type("xsi:type")
+
{
addSynonym(rect, "");
}
@@ -111,7 +120,7 @@ LLView::LLView(const LLView::Params& p)
: mName(p.name),
mParentView(NULL),
mReshapeFlags(FOLLOWS_NONE),
- mSaveToXML(p.serializable),
+ mFromXUI(p.from_xui),
mIsFocusRoot(FALSE),
mLastVisible(FALSE),
mNextInsertionOrdinal(0),
@@ -124,7 +133,7 @@ LLView::LLView(const LLView::Params& p)
mDefaultTabGroup(p.default_tab_group),
mLastTabGroup(0),
mToolTipMsg((LLStringExplicit)p.tool_tip()),
- mDummyWidgets(NULL)
+ mDefaultWidgets(NULL)
{
// create rect first, as this will supply initial follows flags
setShape(p.rect);
@@ -133,17 +142,16 @@ LLView::LLView(const LLView::Params& p)
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
- if( gFocusMgr.getKeyboardFocus() == this )
- {
- //llwarns << "View holding keyboard focus deleted: " << getName() << ". Keyboard focus removed." << llendl;
- gFocusMgr.removeKeyboardFocusWithoutCallback( this );
- }
-
if( hasMouseCapture() )
{
//llwarns << "View holding mouse capture deleted: " << getName() << ". Mouse capture removed." << llendl;
@@ -157,22 +165,16 @@ LLView::~LLView()
mParentView->removeChild(this);
}
- if (mDummyWidgets)
+ if (mDefaultWidgets)
{
- std::for_each(mDummyWidgets->begin(), mDummyWidgets->end(),
+ std::for_each(mDefaultWidgets->begin(), mDefaultWidgets->end(),
DeletePairedPointer());
- delete mDummyWidgets;
- mDummyWidgets = NULL;
+ delete mDefaultWidgets;
+ mDefaultWidgets = NULL;
}
}
// virtual
-BOOL LLView::isView() const
-{
- return TRUE;
-}
-
-// virtual
BOOL LLView::isCtrl() const
{
return FALSE;
@@ -222,10 +224,9 @@ BOOL LLView::getUseBoundingRect()
}
// virtual
-const std::string& LLView::getName() const
+std::string LLView::getName() const
{
- static const std::string unnamed("(no name)");
- return mName.empty() ? unnamed : mName;
+ return mName.empty() ? std::string("(no name)") : mName;
}
void LLView::sendChildToFront(LLView* child)
@@ -398,30 +399,38 @@ bool LLCompareByTabOrder::operator() (const LLView* const a, const LLView* const
BOOL LLView::isInVisibleChain() const
{
- const LLView* cur_view = this;
- while(cur_view)
+ BOOL visible = TRUE;
+
+ const LLView* viewp = this;
+ while(viewp)
{
- if (!cur_view->getVisible())
+ if (!viewp->getVisible())
{
- return FALSE;
+ visible = FALSE;
+ break;
}
- cur_view = cur_view->getParent();
+ viewp = viewp->getParent();
}
- return TRUE;
+
+ return visible;
}
BOOL LLView::isInEnabledChain() const
{
- const LLView* cur_view = this;
- while(cur_view)
+ BOOL enabled = TRUE;
+
+ const LLView* viewp = this;
+ while(viewp)
{
- if (!cur_view->getEnabled())
+ if (!viewp->getEnabled())
{
- return FALSE;
+ enabled = FALSE;
+ break;
}
- cur_view = cur_view->getParent();
+ viewp = viewp->getParent();
}
- return TRUE;
+
+ return enabled;
}
// virtual
@@ -431,20 +440,21 @@ BOOL LLView::canFocusChildren() const
}
//virtual
-void LLView::setTentative(BOOL b)
+void LLView::setEnabled(BOOL enabled)
{
+ mEnabled = enabled;
}
//virtual
-BOOL LLView::getTentative() const
+bool LLView::isAvailable() const
{
- return FALSE;
+ return isInEnabledChain() && isInVisibleChain();
}
-//virtual
-void LLView::setEnabled(BOOL enabled)
+//static
+bool LLView::isAvailable(const LLView* view)
{
- mEnabled = enabled;
+ return view && view->isAvailable();
}
//virtual
@@ -465,16 +475,6 @@ LLRect LLView::getRequiredRect()
return mRect;
}
-//virtual
-void LLView::onFocusLost()
-{
-}
-
-//virtual
-void LLView::onFocusReceived()
-{
-}
-
BOOL LLView::focusNextRoot()
{
LLView::child_list_t result = LLView::getFocusRootsQuery().run(this);
@@ -591,25 +591,21 @@ 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
if (!getParent() || getParent()->isInVisibleChain())
{
// tell all children of this view that the visibility may have changed
- onVisibilityChange( visible );
+ dirtyRect();
+ handleVisibilityChange( visible );
}
updateBoundingRect();
}
}
// virtual
-void LLView::onVisibilityChange ( BOOL new_visibility )
+void LLView::handleVisibilityChange ( BOOL new_visibility )
{
for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
{
@@ -617,7 +613,7 @@ void LLView::onVisibilityChange ( BOOL new_visibility )
// only views that are themselves visible will have their overall visibility affected by their ancestors
if (viewp->getVisible())
{
- viewp->onVisibilityChange ( new_visibility );
+ viewp->handleVisibilityChange ( new_visibility );
}
}
}
@@ -642,16 +638,7 @@ void LLView::setSnappedTo(const LLView* snap_view)
BOOL LLView::handleHover(S32 x, S32 y, MASK mask)
{
- BOOL handled = childrenHandleHover( x, y, mask ) != NULL;
- if( !handled
- && blockMouseEvent(x, y) )
- {
- LLUI::sWindow->setCursor(mHoverCursor);
- lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << llendl;
- handled = TRUE;
- }
-
- return handled;
+ return childrenHandleHover( x, y, mask ) != NULL;
}
void LLView::onMouseEnter(S32 x, S32 y, MASK mask)
@@ -665,98 +652,91 @@ void LLView::onMouseLeave(S32 x, S32 y, MASK mask)
}
-std::string LLView::getShowNamesToolTip()
+LLView* LLView::childrenHandleToolTip(S32 x, S32 y, MASK mask)
{
- LLView* view = getParent();
- std::string name;
- std::string tool_tip = mName;
-
- while (view)
+ LLView* handled_view = NULL;
+ for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
{
- name = view->getName();
-
- if (name == "root") break;
+ LLView* viewp = *child_it;
+ S32 local_x = x - viewp->getRect().mLeft;
+ S32 local_y = y - viewp->getRect().mBottom;
+ if(!viewp->pointInView(local_x, local_y)
+ || !viewp->getVisible())
+ {
+ continue;
+ }
- if (view->getToolTip().find(".xml") != std::string::npos)
+ if (viewp->handleToolTip(local_x, local_y, mask) )
{
- tool_tip = view->getToolTip() + "/" + tool_tip;
+ if (sDebugMouseHandling)
+ {
+ sMouseHandlerMessage = std::string("/") + viewp->mName + sMouseHandlerMessage;
+ }
+
+ handled_view = viewp;
break;
}
- else
+
+ if (viewp->blockMouseEvent(local_x, local_y))
{
- tool_tip = view->getName() + "/" + tool_tip;
+ handled_view = viewp;
+ break;
}
-
- view = view->getParent();
}
-
- return "/" + tool_tip;
+ return handled_view;
}
-BOOL LLView::handleToolTip(S32 x, S32 y, std::string& msg, LLRect* sticky_rect_screen)
+LLView* LLView::childFromPoint(S32 x, S32 y)
{
- BOOL handled = FALSE;
-
- std::string tool_tip;
-
+ if (!getVisible() )
+ return false;
for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
{
LLView* viewp = *child_it;
- S32 local_x = x - viewp->mRect.mLeft;
- S32 local_y = y - viewp->mRect.mBottom;
- // Allow tooltips for disabled views so we can explain to the user why
- // the view is disabled. JC
- if( viewp->pointInView(local_x, local_y)
- && viewp->getVisible()
- // && viewp->getEnabled()
- && viewp->handleToolTip(local_x, local_y, msg, sticky_rect_screen ))
- {
- // child provided a tooltip, just return
- if (!msg.empty()) return TRUE;
-
- // otherwise, one of our children ate the event so don't traverse
- // siblings however, our child did not actually provide a tooltip
- // so we might want to
- handled = TRUE;
- break;
+ S32 local_x = x - viewp->getRect().mLeft;
+ S32 local_y = y - viewp->getRect().mBottom;
+ if (!viewp->pointInView(local_x, local_y)
+ || !viewp->getVisible() )
+ {
+ continue;
}
- }
+ return viewp;
- // get our own tooltip
- tool_tip = mToolTipMsg.getString();
-
- if (LLUI::sShowXUINames
- && (tool_tip.find(".xml", 0) == std::string::npos)
- && (mName.find("Drag", 0) == std::string::npos))
- {
- tool_tip = getShowNamesToolTip();
}
+ return 0;
+}
+
+BOOL LLView::handleToolTip(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = FALSE;
- if(!tool_tip.empty())
+ // parents provide tooltips first, which are optionally
+ // overridden by children, in case child is mouse_opaque
+ if (!mToolTipMsg.empty())
{
- msg = tool_tip;
+ // allow "scrubbing" over ui by showing next tooltip immediately
+ // if previous one was still visible
+ F32 timeout = LLToolTipMgr::instance().toolTipVisible()
+ ? 0.f
+ : LLUI::sSettingGroups["config"]->getF32( "ToolTipDelay" );
+ LLToolTipMgr::instance().show(LLToolTip::Params()
+ .message(mToolTipMsg)
+ .sticky_rect(calcScreenRect())
+ .delay_time(timeout));
- // Convert rect local to screen coordinates
- localPointToScreen(
- 0, 0,
- &(sticky_rect_screen->mLeft), &(sticky_rect_screen->mBottom) );
- localPointToScreen(
- mRect.getWidth(), mRect.getHeight(),
- &(sticky_rect_screen->mRight), &(sticky_rect_screen->mTop) );
+ handled = TRUE;
}
- // don't allow any siblings to handle this event
- // even if we don't have a tooltip
- if (getMouseOpaque() ||
- (!tool_tip.empty() &&
- (!LLUI::sShowXUINames || dynamic_cast<LLTextBox*>(this))))
+
+ // child tooltips will override our own
+ LLView* child_handler = childrenHandleToolTip(x, y, mask);
+ if (child_handler)
{
handled = TRUE;
}
return handled;
}
-
BOOL LLView::handleKey(KEY key, MASK mask, BOOL called_from_parent)
{
BOOL handled = FALSE;
@@ -837,20 +817,7 @@ BOOL LLView::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
EAcceptance* accept,
std::string& tooltip_msg)
{
- // CRO this is an experiment to allow drag and drop into object inventory based on the DragAndDrop tool's permissions rather than the parent
- BOOL handled = childrenHandleDragAndDrop( x, y, mask, drop,
- cargo_type,
- cargo_data,
- accept,
- tooltip_msg) != NULL;
- if( !handled && blockMouseEvent(x, y) )
- {
- *accept = ACCEPT_NO;
- handled = TRUE;
- lldebugst(LLERR_USER_INPUT) << "dragAndDrop handled by LLView " << getName() << llendl;
- }
-
- return handled;
+ return childrenHandleDragAndDrop( x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg) != NULL;
}
LLView* LLView::childrenHandleDragAndDrop(S32 x, S32 y, MASK mask,
@@ -860,28 +827,33 @@ LLView* LLView::childrenHandleDragAndDrop(S32 x, S32 y, MASK mask,
EAcceptance* accept,
std::string& tooltip_msg)
{
- LLView* handled_view = FALSE;
- // CRO this is an experiment to allow drag and drop into object inventory based on the DragAndDrop tool's permissions rather than the parent
- if( getVisible() )
-// if( getVisible() && getEnabled() )
+ LLView* handled_view = NULL;
+ for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
{
- for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
+ LLView* viewp = *child_it;
+ S32 local_x = x - viewp->getRect().mLeft;
+ S32 local_y = y - viewp->getRect().mBottom;
+ if( !viewp->pointInView(local_x, local_y) ||
+ !viewp->getVisible() ||
+ !viewp->getEnabled())
{
- LLView* viewp = *child_it;
- S32 local_x = x - viewp->getRect().mLeft;
- S32 local_y = y - viewp->getRect().mBottom;
- if( viewp->pointInView(local_x, local_y) &&
- viewp->getVisible() &&
- viewp->getEnabled() &&
- viewp->handleDragAndDrop(local_x, local_y, mask, drop,
- cargo_type,
- cargo_data,
- accept,
- tooltip_msg))
- {
- handled_view = viewp;
- break;
- }
+ continue;
+ }
+ if (viewp->handleDragAndDrop(local_x, local_y, mask, drop,
+ cargo_type,
+ cargo_data,
+ accept,
+ tooltip_msg))
+ {
+ handled_view = viewp;
+ break;
+ }
+
+ if (viewp->blockMouseEvent(x, y))
+ {
+ *accept = ACCEPT_NO;
+ handled_view = viewp;
+ break;
}
}
return handled_view;
@@ -898,110 +870,42 @@ BOOL LLView::hasMouseCapture()
BOOL LLView::handleMouseUp(S32 x, S32 y, MASK mask)
{
- BOOL handled = childrenHandleMouseUp( x, y, mask ) != NULL;
- if( !handled && blockMouseEvent(x, y) )
- {
- handled = TRUE;
- }
- return handled;
+ return childrenHandleMouseUp( x, y, mask ) != NULL;
}
BOOL LLView::handleMouseDown(S32 x, S32 y, MASK mask)
{
- LLView* handled_view = childrenHandleMouseDown( x, y, mask );
- BOOL handled = (handled_view != NULL);
- if( !handled && blockMouseEvent(x, y) )
- {
- handled = TRUE;
- handled_view = this;
- }
-
- //// HACK If we're editing UI, select the leaf view that ate the click.
- //if (sEditingUI && handled_view)
- //{
- // // need to find leaf views, big hack
- // LLButton* buttonp = dynamic_cast<LLButton*>(handled_view);
- // LLLineEditor* line_editorp = dynamic_cast<LLLineEditor*>(handled_view);
- // LLTextEditor* text_editorp = dynamic_cast<LLTextEditor*>(handled_view);
- // LLTextBox* text_boxp = dynamic_cast<LLTextBox*>(handled_view);
- // if (buttonp
- // || line_editorp
- // || text_editorp
- // || text_boxp)
- // {
- // sEditingUIView = handled_view;
- // }
- //}
-
- return handled;
+ return childrenHandleMouseDown( x, y, mask ) != NULL;
}
BOOL LLView::handleDoubleClick(S32 x, S32 y, MASK mask)
{
- BOOL handled = childrenHandleDoubleClick( x, y, mask ) != NULL;
- if( !handled && blockMouseEvent(x, y) )
- {
- handleMouseDown(x, y, mask);
- handled = TRUE;
- }
- return handled;
+ return childrenHandleDoubleClick( x, y, mask ) != NULL;
}
BOOL LLView::handleScrollWheel(S32 x, S32 y, S32 clicks)
{
- BOOL handled = FALSE;
- if( getVisible() && getEnabled() )
- {
- handled = childrenHandleScrollWheel( x, y, clicks ) != NULL;
- if( !handled && blockMouseEvent(x, y) )
- {
- handled = TRUE;
- }
- }
- return handled;
+ return childrenHandleScrollWheel( x, y, clicks ) != NULL;
}
BOOL LLView::handleRightMouseDown(S32 x, S32 y, MASK mask)
{
- BOOL handled = childrenHandleRightMouseDown( x, y, mask ) != NULL;
- if( !handled && blockMouseEvent(x, y) )
- {
- handled = TRUE;
- }
- return handled;
+ return childrenHandleRightMouseDown( x, y, mask ) != NULL;
}
BOOL LLView::handleRightMouseUp(S32 x, S32 y, MASK mask)
{
- BOOL handled = childrenHandleRightMouseUp( x, y, mask ) != NULL;
- if( !handled && blockMouseEvent(x, y) )
- {
- handled = TRUE;
- }
- return handled;
+ return childrenHandleRightMouseUp( x, y, mask ) != NULL;
}
BOOL LLView::handleMiddleMouseDown(S32 x, S32 y, MASK mask)
{
- LLView* handled_view = childrenHandleMiddleMouseDown( x, y, mask );
- BOOL handled = (handled_view != NULL);
- if( !handled && blockMouseEvent(x, y) )
- {
- handled = TRUE;
- handled_view = this;
- }
-
- return handled;
+ return childrenHandleMiddleMouseDown( x, y, mask ) != NULL;
}
BOOL LLView::handleMiddleMouseUp(S32 x, S32 y, MASK mask)
{
- BOOL handled = childrenHandleMiddleMouseUp( x, y, mask ) != NULL;
- if( !handled && blockMouseEvent(x, y) )
- {
- handled = TRUE;
- }
- return handled;
+ return childrenHandleMiddleMouseUp( x, y, mask ) != NULL;
}
@@ -1015,14 +919,18 @@ LLView* LLView::childrenHandleScrollWheel(S32 x, S32 y, S32 clicks)
LLView* viewp = *child_it;
S32 local_x = x - viewp->getRect().mLeft;
S32 local_y = y - viewp->getRect().mBottom;
- if (viewp->pointInView(local_x, local_y)
- && viewp->getVisible()
- && viewp->getEnabled()
- && viewp->handleScrollWheel( local_x, local_y, clicks ))
+ if (!viewp->pointInView(local_x, local_y)
+ || !viewp->getVisible()
+ || !viewp->getEnabled())
+ {
+ continue;
+ }
+
+ if (viewp->handleScrollWheel( local_x, local_y, clicks ))
{
if (sDebugMouseHandling)
{
- sMouseHandlerMessage = std::string("->") + viewp->mName + sMouseHandlerMessage;
+ sMouseHandlerMessage = std::string("/") + viewp->mName + sMouseHandlerMessage;
}
handled_view = viewp;
@@ -1043,19 +951,31 @@ LLView* LLView::childrenHandleHover(S32 x, S32 y, MASK mask)
LLView* viewp = *child_it;
S32 local_x = x - viewp->getRect().mLeft;
S32 local_y = y - viewp->getRect().mBottom;
- if(viewp->pointInView(local_x, local_y) &&
- viewp->getVisible() &&
- viewp->getEnabled() &&
- viewp->handleHover(local_x, local_y, mask) )
+ if(!viewp->pointInView(local_x, local_y)
+ || !viewp->getVisible()
+ || !viewp->getEnabled())
+ {
+ continue;
+ }
+
+ if (viewp->handleHover(local_x, local_y, mask) )
{
if (sDebugMouseHandling)
{
- sMouseHandlerMessage = std::string("->") + viewp->mName + sMouseHandlerMessage;
+ sMouseHandlerMessage = std::string("/") + viewp->mName + sMouseHandlerMessage;
}
handled_view = viewp;
break;
}
+
+ if (viewp->blockMouseEvent(local_x, local_y))
+ {
+ LLUI::sWindow->setCursor(viewp->getHoverCursor());
+
+ handled_view = viewp;
+ break;
+ }
}
}
return handled_view;
@@ -1121,18 +1041,28 @@ LLView* LLView::childrenHandleMouseDown(S32 x, S32 y, MASK mask)
S32 local_x = x - viewp->getRect().mLeft;
S32 local_y = y - viewp->getRect().mBottom;
- if (viewp->pointInView(local_x, local_y) &&
- viewp->getVisible() &&
- viewp->getEnabled() &&
- viewp->handleMouseDown( local_x, local_y, mask ))
+ if (!viewp->pointInView(local_x, local_y)
+ || !viewp->getVisible()
+ || !viewp->getEnabled())
+ {
+ continue;
+ }
+
+ if(viewp->handleMouseDown( local_x, local_y, mask ))
{
if (sDebugMouseHandling)
{
- sMouseHandlerMessage = std::string("->") + viewp->mName + sMouseHandlerMessage;
+ sMouseHandlerMessage = std::string("/") + viewp->mName + sMouseHandlerMessage;
}
handled_view = viewp;
break;
}
+
+ if(viewp->blockMouseEvent(local_x, local_y))
+ {
+ handled_view = viewp;
+ break;
+ }
}
return handled_view;
}
@@ -1148,19 +1078,30 @@ LLView* LLView::childrenHandleRightMouseDown(S32 x, S32 y, MASK mask)
LLView* viewp = *child_it;
S32 local_x = x - viewp->getRect().mLeft;
S32 local_y = y - viewp->getRect().mBottom;
- if (viewp->pointInView(local_x, local_y) &&
- viewp->getVisible() &&
- viewp->getEnabled() &&
- viewp->handleRightMouseDown( local_x, local_y, mask ))
+
+ if (!viewp->pointInView(local_x, local_y)
+ || !viewp->getVisible()
+ || !viewp->getEnabled())
+ {
+ continue;
+ }
+
+ if (viewp->handleRightMouseDown( local_x, local_y, mask ))
{
if (sDebugMouseHandling)
{
- sMouseHandlerMessage = std::string("->") + viewp->mName + sMouseHandlerMessage;
+ sMouseHandlerMessage = std::string("/") + viewp->mName + sMouseHandlerMessage;
}
handled_view = viewp;
break;
}
+
+ if (viewp->blockMouseEvent(local_x, local_y))
+ {
+ handled_view = viewp;
+ break;
+ }
}
}
return handled_view;
@@ -1177,18 +1118,28 @@ LLView* LLView::childrenHandleMiddleMouseDown(S32 x, S32 y, MASK mask)
LLView* viewp = *child_it;
S32 local_x = x - viewp->getRect().mLeft;
S32 local_y = y - viewp->getRect().mBottom;
- if (viewp->pointInView(local_x, local_y) &&
- viewp->getVisible() &&
- viewp->getEnabled() &&
- viewp->handleMiddleMouseDown( local_x, local_y, mask ))
+ if (!viewp->pointInView(local_x, local_y)
+ || !viewp->getVisible()
+ || !viewp->getEnabled())
+ {
+ continue;
+ }
+
+ if(viewp->handleMiddleMouseDown( local_x, local_y, mask ))
{
if (sDebugMouseHandling)
{
- sMouseHandlerMessage = std::string("->") + viewp->mName + sMouseHandlerMessage;
+ sMouseHandlerMessage = std::string("/") + viewp->mName + sMouseHandlerMessage;
}
handled_view = viewp;
break;
}
+
+ if (viewp->blockMouseEvent(local_x, local_y))
+ {
+ handled_view = viewp;
+ break;
+ }
}
}
return handled_view;
@@ -1205,18 +1156,29 @@ LLView* LLView::childrenHandleDoubleClick(S32 x, S32 y, MASK mask)
LLView* viewp = *child_it;
S32 local_x = x - viewp->getRect().mLeft;
S32 local_y = y - viewp->getRect().mBottom;
- if (viewp->pointInView(local_x, local_y) &&
- viewp->getVisible() &&
- viewp->getEnabled() &&
- viewp->handleDoubleClick( local_x, local_y, mask ))
+
+ if (!viewp->pointInView(local_x, local_y)
+ || !viewp->getVisible()
+ || !viewp->getEnabled())
+ {
+ continue;
+ }
+
+ if (viewp->handleDoubleClick( local_x, local_y, mask ))
{
if (sDebugMouseHandling)
{
- sMouseHandlerMessage = std::string("->") + viewp->mName + sMouseHandlerMessage;
+ sMouseHandlerMessage = std::string("/") + viewp->mName + sMouseHandlerMessage;
}
handled_view = viewp;
break;
}
+
+ if (viewp->blockMouseEvent(local_x, local_y))
+ {
+ handled_view = viewp;
+ break;
+ }
}
}
return handled_view;
@@ -1232,21 +1194,28 @@ LLView* LLView::childrenHandleMouseUp(S32 x, S32 y, MASK mask)
LLView* viewp = *child_it;
S32 local_x = x - viewp->getRect().mLeft;
S32 local_y = y - viewp->getRect().mBottom;
- if (!viewp->pointInView(local_x, local_y))
- continue;
- if (!viewp->getVisible())
- continue;
- if (!viewp->getEnabled())
+ if (!viewp->pointInView(local_x, local_y)
+ || !viewp->getVisible()
+ || !viewp->getEnabled())
+ {
continue;
+ }
+
if (viewp->handleMouseUp( local_x, local_y, mask ))
{
if (sDebugMouseHandling)
{
- sMouseHandlerMessage = std::string("->") + viewp->mName + sMouseHandlerMessage;
+ sMouseHandlerMessage = std::string("/") + viewp->mName + sMouseHandlerMessage;
}
handled_view = viewp;
break;
}
+
+ if (viewp->blockMouseEvent(local_x, local_y))
+ {
+ handled_view = viewp;
+ break;
+ }
}
}
return handled_view;
@@ -1262,18 +1231,28 @@ LLView* LLView::childrenHandleRightMouseUp(S32 x, S32 y, MASK mask)
LLView* viewp = *child_it;
S32 local_x = x - viewp->getRect().mLeft;
S32 local_y = y - viewp->getRect().mBottom;
- if (viewp->pointInView(local_x, local_y) &&
- viewp->getVisible() &&
- viewp->getEnabled() &&
- viewp->handleRightMouseUp( local_x, local_y, mask ))
+ if (!viewp->pointInView(local_x, local_y)
+ || !viewp->getVisible()
+ || !viewp->getEnabled() )
+ {
+ continue;
+ }
+
+ if(viewp->handleRightMouseUp( local_x, local_y, mask ))
{
if (sDebugMouseHandling)
{
- sMouseHandlerMessage = std::string("->") + viewp->mName + sMouseHandlerMessage;
+ sMouseHandlerMessage = std::string("/") + viewp->mName + sMouseHandlerMessage;
}
handled_view = viewp;
break;
}
+
+ if(viewp->blockMouseEvent(local_x, local_y))
+ {
+ handled_view = viewp;
+ break;
+ }
}
}
return handled_view;
@@ -1289,18 +1268,28 @@ LLView* LLView::childrenHandleMiddleMouseUp(S32 x, S32 y, MASK mask)
LLView* viewp = *child_it;
S32 local_x = x - viewp->getRect().mLeft;
S32 local_y = y - viewp->getRect().mBottom;
- if (viewp->pointInView(local_x, local_y) &&
- viewp->getVisible() &&
- viewp->getEnabled() &&
- viewp->handleMiddleMouseUp( local_x, local_y, mask ))
+ if (!viewp->pointInView(local_x, local_y)
+ || !viewp->getVisible()
+ || !viewp->getEnabled())
+ {
+ continue;
+ }
+
+ if(viewp->handleMiddleMouseUp( local_x, local_y, mask ))
{
if (sDebugMouseHandling)
{
- sMouseHandlerMessage = std::string("->") + viewp->mName + sMouseHandlerMessage;
+ sMouseHandlerMessage = std::string("/") + viewp->mName + sMouseHandlerMessage;
}
handled_view = viewp;
break;
}
+
+ if (viewp->blockMouseEvent(local_x, local_y))
+ {
+ handled_view = viewp;
+ break;
+ }
}
}
return handled_view;
@@ -1308,30 +1297,16 @@ LLView* LLView::childrenHandleMiddleMouseUp(S32 x, S32 y, MASK mask)
void LLView::draw()
{
- if (sDebugRects)
- {
- drawDebugRect();
-
- // Check for bogus rectangle
- if (getRect().mRight <= getRect().mLeft
- || getRect().mTop <= getRect().mBottom)
- {
- llwarns << "Bogus rectangle for " << getName() << " with " << mRect << llendl;
- }
- }
+ drawChildren();
+}
+void LLView::drawChildren()
+{
if (!mChildList.empty())
{
LLRect rootRect = getRootView()->getRect();
LLRect screenRect;
- // draw focused control on top of everything else
- LLView* focus_view = gFocusMgr.getKeyboardFocus();
- if (focus_view && focus_view->getParent() != this)
- {
- focus_view = NULL;
- }
-
++sDepth;
for (child_list_reverse_iter_t child_iter = mChildList.rbegin(); child_iter != mChildList.rend();) // ++child_iter)
@@ -1339,17 +1314,27 @@ void LLView::draw()
child_list_reverse_iter_t child = child_iter++;
LLView *viewp = *child;
- if (viewp->getVisible() && viewp != focus_view && viewp->getRect().isValid())
+ if (viewp->getVisible() && viewp->getRect().isValid())
{
// Only draw views that are within the root view
localRectToScreen(viewp->getRect(),&screenRect);
- if ( rootRect.rectInRect(&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);
viewp->draw();
+
+ if (sDebugRects)
+ {
+ viewp->drawDebugRect();
+
+ // Check for bogus rectangle
+ if (!getRect().isValid())
+ {
+ llwarns << "Bogus rectangle for " << getName() << " with " << mRect << llendl;
+ }
+ }
}
LLUI::popMatrix();
}
@@ -1357,14 +1342,22 @@ void LLView::draw()
}
--sDepth;
+ }
+}
- if (focus_view && focus_view->getVisible())
- {
- drawChild(focus_view);
- }
+void LLView::dirtyRect()
+{
+ LLView* child = getParent();
+ LLView* parent = child ? child->getParent() : NULL;
+ LLView* cur = this;
+ while (child && parent && parent->getParent())
+ { //find third to top-most view
+ cur = child;
+ child = parent;
+ parent = parent->getParent();
}
- gGL.getTexUnit(0)->disable();
+ LLUI::dirtyRect(cur->calcScreenRect());
}
//Draw a box for debugging.
@@ -1385,11 +1378,7 @@ void LLView::drawDebugRect()
LLRect debug_rect = mUseBoundingRect ? mBoundingRect : mRect;
// draw red rectangle for the border
- LLColor4 border_color(0.f, 0.f, 0.f, 1.f);
- //if (sEditingUI)
- //{
- // border_color.mV[0] = 1.f;
- //}
+ LLColor4 border_color(0.25f, 0.25f, 0.25f, 1.f);
if(preview_iter != sPreviewHighlightedElements.end())
{
if(LLView::sPreviewClickedElement && this == sPreviewClickedElement)
@@ -1398,7 +1387,7 @@ void LLView::drawDebugRect()
}
else
{
- static LLUICachedControl<LLColor4> scroll_highlighted_color ("ScrollHighlightedColor", *(new LLColor4));
+ static LLUIColor scroll_highlighted_color = LLUIColorTable::instance().getColor("ScrollHighlightedColor");
border_color = scroll_highlighted_color;
}
}
@@ -1424,7 +1413,9 @@ void LLView::drawDebugRect()
gGL.end();
// Draw the name if it's not a leaf node or not in editing or preview mode
- if (mChildList.size() && preview_iter == sPreviewHighlightedElements.end())
+ if (mChildList.size()
+ && preview_iter == sPreviewHighlightedElements.end()
+ && sDebugRectsShowNames)
{
//char temp[256];
S32 x, y;
@@ -1538,45 +1529,53 @@ void LLView::reshape(S32 width, S32 height, BOOL called_from_parent)
updateBoundingRect();
}
-void LLView::updateBoundingRect()
+LLRect LLView::calcBoundingRect()
{
- if (isDead()) return;
+ LLRect local_bounding_rect = LLRect::null;
- if (mUseBoundingRect)
+ child_list_const_iter_t child_it;
+ for ( child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
{
- LLRect local_bounding_rect = LLRect::null;
-
- child_list_const_iter_t child_it;
- for ( child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
+ LLView* childp = *child_it;
+ // ignore invisible and "top" children when calculating bounding rect
+ // such as combobox popups
+ if (!childp->getVisible() || childp == gFocusMgr.getTopCtrl())
{
- LLView* childp = *child_it;
- // ignore invisible and "top" children when calculating bounding rect
- // such as combobox popups
- if (!childp->getVisible() || childp == gFocusMgr.getTopCtrl())
- {
- continue;
- }
+ continue;
+ }
- LLRect child_bounding_rect = childp->getBoundingRect();
+ LLRect child_bounding_rect = childp->getBoundingRect();
- if (local_bounding_rect.isNull())
- {
- // start out with bounding rect equal to first visible child's bounding rect
- local_bounding_rect = child_bounding_rect;
- }
- else
+ if (local_bounding_rect.isEmpty())
+ {
+ // start out with bounding rect equal to first visible child's bounding rect
+ local_bounding_rect = child_bounding_rect;
+ }
+ else
+ {
+ // accumulate non-null children rectangles
+ if (!child_bounding_rect.isEmpty())
{
- // accumulate non-null children rectangles
- if (!child_bounding_rect.isNull())
- {
- local_bounding_rect.unionWith(child_bounding_rect);
- }
+ local_bounding_rect.unionWith(child_bounding_rect);
}
}
+ }
+
+ // convert to parent-relative coordinates
+ local_bounding_rect.translate(mRect.mLeft, mRect.mBottom);
+ return local_bounding_rect;
+}
+
+
+void LLView::updateBoundingRect()
+{
+ if (isDead()) return;
+
+ LLRect cur_rect = mBoundingRect;
- mBoundingRect = local_bounding_rect;
- // translate into parent-relative coordinates
- mBoundingRect.translate(mRect.mLeft, mRect.mBottom);
+ if (mUseBoundingRect)
+ {
+ mBoundingRect = calcBoundingRect();
}
else
{
@@ -1588,6 +1587,12 @@ void LLView::updateBoundingRect()
{
getParent()->updateBoundingRect();
}
+
+ if (mBoundingRect != cur_rect)
+ {
+ dirtyRect();
+ }
+
}
LLRect LLView::calcScreenRect() const
@@ -1658,7 +1663,7 @@ BOOL LLView::hasAncestor(const LLView* parentp) const
BOOL LLView::childHasKeyboardFocus( const std::string& childname ) const
{
- LLView *child = getChildView(childname, TRUE, FALSE);
+ LLView *child = findChildView(childname, TRUE);
if (child)
{
return gFocusMgr.childHasKeyboardFocus(child);
@@ -1673,14 +1678,31 @@ BOOL LLView::childHasKeyboardFocus( const std::string& childname ) const
BOOL LLView::hasChild(const std::string& childname, BOOL recurse) const
{
- return getChildView(childname, recurse, FALSE) != NULL;
+ return findChildView(childname, recurse) != NULL;
}
//-----------------------------------------------------------------------------
// getChildView()
//-----------------------------------------------------------------------------
-LLView* LLView::getChildView(const std::string& name, BOOL recurse, BOOL create_if_missing) const
+LLView* LLView::getChildView(const std::string& name, BOOL recurse) const
+{
+ LLView* child = findChildView(name, recurse);
+ if (!child)
+ {
+ child = getDefaultWidget<LLView>(name);
+ if (!child)
+ {
+ child = LLUICtrlFactory::createDefaultWidget<LLView>(name);
+ }
+ }
+ return child;
+}
+
+static LLFastTimer::DeclareTimer FTM_FIND_VIEWS("Find Widgets");
+
+LLView* LLView::findChildView(const std::string& name, BOOL recurse) const
{
+ LLFastTimer ft(FTM_FIND_VIEWS);
//richard: should we allow empty names?
//if(name.empty())
// return NULL;
@@ -1689,6 +1711,7 @@ LLView* LLView::getChildView(const std::string& name, BOOL recurse, BOOL create_
for ( child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
{
LLView* childp = *child_it;
+ llassert(childp);
if (childp->getName() == name)
{
return childp;
@@ -1700,23 +1723,14 @@ LLView* LLView::getChildView(const std::string& name, BOOL recurse, BOOL create_
for ( child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
{
LLView* childp = *child_it;
- LLView* viewp = childp->getChildView(name, recurse, FALSE);
+ llassert(childp);
+ LLView* viewp = childp->findChildView(name, recurse);
if ( viewp )
{
return viewp;
}
}
}
-
- if (create_if_missing)
- {
- LLView* view = getDummyWidget<LLView>(name);
- if (!view)
- {
- view = LLUICtrlFactory::createDummyWidget<LLView>(name);
- }
- return view;
- }
return NULL;
}
@@ -1833,73 +1847,123 @@ void LLView::deleteViewByHandle(LLHandle<LLView> handle)
}
-// Moves the view so that it is entirely inside of constraint.
-// If the view will not fit because it's too big, aligns with the top and left.
-// (Why top and left? That's where the drag bars are for floaters.)
-BOOL LLView::translateIntoRect(const LLRect& constraint, BOOL allow_partial_outside )
+LLCoordGL getNeededTranslation(const LLRect& input, const LLRect& constraint, BOOL allow_partial_outside)
{
- S32 delta_x = 0;
- S32 delta_y = 0;
+ LLCoordGL delta;
if (allow_partial_outside)
{
const S32 KEEP_ONSCREEN_PIXELS = 16;
- if( getRect().mRight - KEEP_ONSCREEN_PIXELS < constraint.mLeft )
+ if( input.mRight - KEEP_ONSCREEN_PIXELS < constraint.mLeft )
{
- delta_x = constraint.mLeft - (getRect().mRight - KEEP_ONSCREEN_PIXELS);
+ delta.mX = constraint.mLeft - (input.mRight - KEEP_ONSCREEN_PIXELS);
}
else
- if( getRect().mLeft + KEEP_ONSCREEN_PIXELS > constraint.mRight )
+ if( input.mLeft + KEEP_ONSCREEN_PIXELS > constraint.mRight )
{
- delta_x = constraint.mRight - (getRect().mLeft + KEEP_ONSCREEN_PIXELS);
+ delta.mX = constraint.mRight - (input.mLeft + KEEP_ONSCREEN_PIXELS);
}
- if( getRect().mTop > constraint.mTop )
+ if( input.mTop > constraint.mTop )
{
- delta_y = constraint.mTop - getRect().mTop;
+ delta.mY = constraint.mTop - input.mTop;
}
else
- if( getRect().mTop - KEEP_ONSCREEN_PIXELS < constraint.mBottom )
+ if( input.mTop - KEEP_ONSCREEN_PIXELS < constraint.mBottom )
{
- delta_y = constraint.mBottom - (getRect().mTop - KEEP_ONSCREEN_PIXELS);
+ delta.mY = constraint.mBottom - (input.mTop - KEEP_ONSCREEN_PIXELS);
}
}
else
{
- if( getRect().mLeft < constraint.mLeft )
+ if( input.mLeft < constraint.mLeft )
{
- delta_x = constraint.mLeft - getRect().mLeft;
+ delta.mX = constraint.mLeft - input.mLeft;
}
else
- if( getRect().mRight > constraint.mRight )
+ if( input.mRight > constraint.mRight )
{
- delta_x = constraint.mRight - getRect().mRight;
+ delta.mX = constraint.mRight - input.mRight;
// compensate for left edge possible going off screen
- delta_x += llmax( 0, getRect().getWidth() - constraint.getWidth() );
+ delta.mX += llmax( 0, input.getWidth() - constraint.getWidth() );
}
- if( getRect().mTop > constraint.mTop )
+ if( input.mTop > constraint.mTop )
{
- delta_y = constraint.mTop - getRect().mTop;
+ delta.mY = constraint.mTop - input.mTop;
}
else
- if( getRect().mBottom < constraint.mBottom )
+ if( input.mBottom < constraint.mBottom )
{
- delta_y = constraint.mBottom - getRect().mBottom;
+ delta.mY = constraint.mBottom - input.mBottom;
// compensate for top edge possible going off screen
- delta_y -= llmax( 0, getRect().getHeight() - constraint.getHeight() );
+ delta.mY -= llmax( 0, input.getHeight() - constraint.getHeight() );
}
}
- if (delta_x != 0 || delta_y != 0)
+ return delta;
+}
+
+// Moves the view so that it is entirely inside of constraint.
+// If the view will not fit because it's too big, aligns with the top and left.
+// (Why top and left? That's where the drag bars are for floaters.)
+BOOL LLView::translateIntoRect(const LLRect& constraint, BOOL allow_partial_outside )
+{
+ LLCoordGL translation = getNeededTranslation(getRect(), constraint, allow_partial_outside);
+
+ if (translation.mX != 0 || translation.mY != 0)
+ {
+ translate(translation.mX, translation.mY);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+// move this view into "inside" but not onto "exclude"
+// NOTE: if this view is already contained in "inside", we ignore the "exclude" rect
+BOOL LLView::translateIntoRectWithExclusion( const LLRect& inside, const LLRect& exclude, BOOL allow_partial_outside )
+{
+ LLCoordGL translation = getNeededTranslation(getRect(), inside, allow_partial_outside);
+
+ if (translation.mX != 0 || translation.mY != 0)
{
- translate(delta_x, delta_y);
+ // translate ourselves into constraint rect
+ translate(translation.mX, translation.mY);
+
+ // do we overlap with exclusion area?
+ // keep moving in the same direction to the other side of the exclusion rect
+ if (exclude.overlaps(getRect()))
+ {
+ // moving right
+ if (translation.mX > 0)
+ {
+ translate(exclude.mRight - getRect().mLeft, 0);
+ }
+ // moving left
+ else if (translation.mX < 0)
+ {
+ translate(exclude.mLeft - getRect().mRight, 0);
+ }
+
+ // moving up
+ if (translation.mY > 0)
+ {
+ translate(0, exclude.mTop - getRect().mBottom);
+ }
+ // moving down
+ else if (translation.mY < 0)
+ {
+ translate(0, exclude.mBottom - getRect().mTop);
+ }
+ }
+
return TRUE;
}
return FALSE;
}
+
void LLView::centerWithin(const LLRect& bounds)
{
S32 left = bounds.mLeft + (bounds.getWidth() - getRect().getWidth()) / 2;
@@ -2303,13 +2367,6 @@ LLControlVariable *LLView::findControl(const std::string& name)
return control_group.getControl(name);
}
-const widget_registry_t& LLView::getChildRegistry() const
-{
- static widget_registry_t empty_registry;
- return empty_registry;
-}
-
-
const S32 FLOATER_H_MARGIN = 15;
const S32 MIN_WIDGET_HEIGHT = 10;
const S32 VPAD = 4;
@@ -2415,7 +2472,7 @@ static bool get_last_child_rect(LLView* parent, LLRect *rect)
for (;itor != parent->getChildList()->end(); ++itor)
{
LLView *last_view = (*itor);
- if (last_view->getSaveToXML())
+ if (last_view->getFromXUI())
{
*rect = last_view->getRect();
return true;
@@ -2425,13 +2482,11 @@ static bool get_last_child_rect(LLView* parent, LLRect *rect)
}
//static
-void LLView::setupParams(LLView::Params& p, LLView* parent)
+void LLView::applyXUILayout(LLView::Params& p, LLView* parent)
{
const S32 VPAD = 4;
const S32 MIN_WIDGET_HEIGHT = 10;
- p.serializable(true);
-
// *NOTE: This will confuse export of floater/panel coordinates unless
// the default is also "topleft". JC
if (p.layout().empty() && parent)
@@ -2446,6 +2501,14 @@ void LLView::setupParams(LLView::Params& p, LLView* parent)
LLRect last_rect = parent->getLocalRect();
bool layout_topleft = (p.layout() == "topleft");
+
+ // convert negative or centered coordinates to parent relative values
+ // Note: some of this logic matches the logic in TypedParam<LLRect>::setValueFromBlock()
+ if (p.rect.left.isProvided() && p.rect.left < 0) p.rect.left = p.rect.left + parent_rect.getWidth();
+ if (p.rect.right.isProvided() && p.rect.right < 0) p.rect.right = p.rect.right + parent_rect.getWidth();
+ if (p.rect.bottom.isProvided() && p.rect.bottom < 0) p.rect.bottom = p.rect.bottom + parent_rect.getHeight();
+ if (p.rect.top.isProvided() && p.rect.top < 0) p.rect.top = p.rect.top + parent_rect.getHeight();
+
if (layout_topleft)
{
//invert top to bottom
@@ -2453,53 +2516,8 @@ void LLView::setupParams(LLView::Params& p, LLView* parent)
if (p.rect.bottom.isProvided()) p.rect.bottom = parent_rect.getHeight() - p.rect.bottom;
}
- // convert negative or centered coordinates to parent relative values
- // Note: some of this logic matches the logic in TypedParam<LLRect>::getValueFromBlock()
-
- if (p.center_horiz)
- {
- if (p.rect.left.isProvided() && p.rect.right.isProvided())
- {
- S32 width = p.rect.right - p.rect.left;
- width = llmax(width, 0);
- S32 offset = parent_rect.getWidth()/2 - width/2;
- p.rect.left = p.rect.left + offset;
- p.rect.right = p.rect.right + offset;
- }
- else
- {
- p.rect.left = p.rect.left + parent_rect.getWidth()/2 - p.rect.width/2;
- }
- }
- else
- {
- if (p.rect.left < 0) p.rect.left = p.rect.left + parent_rect.getWidth();
- if (p.rect.right < 0) p.rect.right = p.rect.right + parent_rect.getWidth();
- }
- if (p.center_vert)
- {
- if (p.rect.bottom.isProvided() && p.rect.top.isProvided())
- {
- S32 height = p.rect.top - p.rect.bottom;
- height = llmax(height, 0);
- S32 offset = parent_rect.getHeight()/2 - height/2;
- p.rect.bottom = p.rect.bottom + offset;
- p.rect.top = p.rect.top + offset;
- }
- else
- {
- p.rect.bottom = p.rect.bottom + parent_rect.getHeight()/2 - p.rect.height/2;
- }
- }
- else
- {
- if (p.rect.bottom < 0) p.rect.bottom = p.rect.bottom + parent_rect.getHeight();
- if (p.rect.top < 0) p.rect.top = p.rect.top + parent_rect.getHeight();
- }
-
-
// DEPRECATE: automatically fall back to height of MIN_WIDGET_HEIGHT pixels
- if (!p.rect.height.isProvided() && !p.rect.top.isProvided())
+ if (!p.rect.height.isProvided() && !p.rect.top.isProvided() && p.rect.height == 0)
{
p.rect.height = MIN_WIDGET_HEIGHT;
}
@@ -2598,7 +2616,7 @@ static void convert_to_relative_layout(LLView::Params& p, LLView* parent)
// Use setupParams to get the final widget rectangle
// according to our wacky layout rules.
LLView::Params final = p;
- LLView::setupParams(final, parent);
+ LLView::applyXUILayout(final, parent);
// Must actually extract the rectangle to get consistent
// right = left+width, top = bottom+height
LLRect final_rect = final.rect;
@@ -2735,26 +2753,97 @@ void LLView::setupParamsForExport(Params& p, LLView* parent)
convert_coords_to_top_left(p, parent);
}
-LLView::tree_iterator_t LLView::beginTree()
+LLView::tree_iterator_t LLView::beginTreeDFS()
{
return tree_iterator_t(this,
boost::bind(boost::mem_fn(&LLView::beginChild), _1),
boost::bind(boost::mem_fn(&LLView::endChild), _1));
}
-LLView::tree_iterator_t LLView::endTree()
+LLView::tree_iterator_t LLView::endTreeDFS()
{
// an empty iterator is an "end" iterator
return tree_iterator_t();
}
+LLView::tree_post_iterator_t LLView::beginTreeDFSPost()
+{
+ return tree_post_iterator_t(this,
+ boost::bind(boost::mem_fn(&LLView::beginChild), _1),
+ boost::bind(boost::mem_fn(&LLView::endChild), _1));
+}
+
+LLView::tree_post_iterator_t LLView::endTreeDFSPost()
+{
+ // an empty iterator is an "end" iterator
+ return tree_post_iterator_t();
+}
+
+LLView::bfs_tree_iterator_t LLView::beginTreeBFS()
+{
+ return bfs_tree_iterator_t(this,
+ boost::bind(boost::mem_fn(&LLView::beginChild), _1),
+ boost::bind(boost::mem_fn(&LLView::endChild), _1));
+}
+
+LLView::bfs_tree_iterator_t LLView::endTreeBFS()
+{
+ // an empty iterator is an "end" iterator
+ return bfs_tree_iterator_t();
+}
+
+
+LLView::root_to_view_iterator_t LLView::beginRootToView()
+{
+ return root_to_view_iterator_t(this, boost::bind(&LLView::getParent, _1));
+}
+
+LLView::root_to_view_iterator_t LLView::endRootToView()
+{
+ return root_to_view_iterator_t();
+}
+
+
// only create maps on demand, as they incur heap allocation/deallocation cost
// when a view is constructed/deconstructed
-LLView::dummy_widget_map_t& LLView::getDummyWidgetMap() const
+LLView::default_widget_map_t& LLView::getDefaultWidgetMap() const
{
- if (!mDummyWidgets)
+ if (!mDefaultWidgets)
{
- mDummyWidgets = new dummy_widget_map_t();
+ mDefaultWidgets = new default_widget_map_t();
}
- return *mDummyWidgets;
+ return *mDefaultWidgets;
+}
+
+S32 LLView::notifyParent(const LLSD& info)
+{
+ LLView* parent = getParent();
+ if(parent)
+ return parent->notifyParent(info);
+ return 0;
+}
+bool LLView::notifyChildren(const LLSD& info)
+{
+ bool ret = false;
+ for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
+ {
+ ret |= (*child_it)->notifyChildren(info);
+ }
+ return ret;
+}
+
+// convenient accessor for draw context
+const LLViewDrawContext& LLView::getDrawContext()
+{
+ return LLViewDrawContext::getCurrentContext();
+}
+
+const LLViewDrawContext& LLViewDrawContext::getCurrentContext()
+{
+ static LLViewDrawContext default_context;
+
+ if (sDrawContextStack.empty())
+ return default_context;
+
+ return *sDrawContextStack.back();
}
diff --git a/indra/llui/llview.h b/indra/llui/llview.h
index 458d02d001..f7175112bf 100644
--- a/indra/llui/llview.h
+++ b/indra/llui/llview.h
@@ -2,31 +2,25 @@
* @file llview.h
* @brief Container for other views, anything that draws.
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -42,8 +36,6 @@
#include "llfontgl.h"
#include "llmortician.h"
#include "llmousehandler.h"
-#include "llnametable.h"
-#include "llsd.h"
#include "llstring.h"
#include "llrect.h"
#include "llui.h"
@@ -54,9 +46,12 @@
#include "llcursortypes.h"
#include "lluictrlfactory.h"
#include "lltreeiterators.h"
+#include "llfocusmgr.h"
#include <list>
+class LLSD;
+
const U32 FOLLOWS_NONE = 0x00;
const U32 FOLLOWS_LEFT = 0x01;
const U32 FOLLOWS_RIGHT = 0x02;
@@ -69,87 +64,48 @@ const BOOL NOT_MOUSE_OPAQUE = FALSE;
const U32 GL_NAME_UI_RESERVED = 2;
-/*
-// virtual functions defined in LLView:
-
-virtual BOOL isCtrl() const;
- LLUICtrl
-virtual BOOL isPanel();
- LLPanel
-virtual void setRect(const LLRect &rect);
- LLLineEditor
- LLPanel
-virtual BOOL canFocusChildren() const { return TRUE; }
- LLFolderView
-virtual void deleteAllChildren();
- LLFolderView, LLPanelInventory
-virtual void setTentative(BOOL b) {}
- LLUICtrl, LLSliderCtrl, LLSpinCtrl
-virtual BOOL getTentative() const { return FALSE; }
- LLUICtrl, LLCheckBoxCtrl
-virtual void setVisible(BOOL visible);
- LLFloater, LLAlertDialog, LLMenuItemGL, LLModalDialog
-virtual void setEnabled(BOOL enabled) { mEnabled = enabled; }
- LLCheckBoxCtrl, LLComboBox, LLLineEditor, LLMenuGL, LLRadioGroup, etc
-virtual BOOL setLabelArg( const std::string& key, const LLStringExplicit& text ) { return FALSE; }
- LLUICtrl, LLButton, LLCheckBoxCtrl, LLLineEditor, LLMenuGL, LLSliderCtrl
-virtual void onVisibilityChange ( BOOL curVisibilityIn );
- LLMenuGL
-virtual LLRect getSnapRect() const { return mRect; } *TODO: Make non virtual
- LLFloater
-virtual LLRect getRequiredRect() { return mRect; }
- LLScrolllistCtrl
-virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
- LLUICtrl, et. al.
-virtual void translate( S32 x, S32 y );
- LLMenuGL
-virtual void setShape(const LLRect& new_rect, bool by_user);
- LLFloater, LLScrollLIstVtrl
-virtual LLView* findSnapRect(LLRect& new_rect, const LLCoordGL& mouse_dir, LLView::ESnapType snap_type, S32 threshold, S32 padding = 0);
-virtual LLView* findSnapEdge(S32& new_edge_val, const LLCoordGL& mouse_dir, ESnapEdge snap_edge, ESnapType snap_type, S32 threshold, S32 padding = 0);
- LLScrollListCtrl
-virtual BOOL canSnapTo(const LLView* other_view) { return other_view != this && other_view->getVisible(); }
- LLFloater
-virtual void snappedTo(const LLView* snap_view) {}
- LLFloater
-virtual BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent);
- *
-virtual BOOL handleUnicodeChar(llwchar uni_char, BOOL called_from_parent);
- *
-virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,EDragAndDropType cargo_type,void* cargo_data,EAcceptance* accept,std::string& tooltip_msg);
- *
-virtual void draw();
- *
-
- *
-virtual void onFocusLost() {}
- LLUICtrl, LLScrollListCtrl, LLMenuGL, LLLineEditor, LLComboBox
-virtual void onFocusReceived() {}
- LLUICtrl, LLTextEditor, LLScrollListVtrl, LLMenuGL, LLLineEditor
-virtual LLView* getChildView(const std::string& name, BOOL recurse = TRUE, BOOL create_if_missing = TRUE) const;
- LLTabContainer, LLPanel, LLMenuGL
-virtual bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata);
- LLMenuItem
-protected:
-virtual BOOL handleKeyHere(KEY key, MASK mask);
- *
-virtual BOOL handleUnicodeCharHere(llwchar uni_char);
- *
-*/
+// maintains render state during traversal of UI tree
+class LLViewDrawContext
+{
+public:
+ F32 mAlpha;
+
+ LLViewDrawContext(F32 alpha = 1.f)
+ : mAlpha(alpha)
+ {
+ if (!sDrawContextStack.empty())
+ {
+ LLViewDrawContext* context_top = sDrawContextStack.back();
+ // merge with top of stack
+ mAlpha *= context_top->mAlpha;
+ }
+ sDrawContextStack.push_back(this);
+ }
+
+ ~LLViewDrawContext()
+ {
+ sDrawContextStack.pop_back();
+ }
+
+ static const LLViewDrawContext& getCurrentContext();
-class LLView : public LLMouseHandler, public LLMortician
+private:
+ static std::vector<LLViewDrawContext*> sDrawContextStack;
+};
+
+class LLViewWidgetRegistry : public LLChildRegistry<LLViewWidgetRegistry>
+{};
+
+class LLView : public LLMouseHandler, public LLMortician, public LLFocusableElement
{
public:
struct Follows : public LLInitParam::Choice<Follows>
{
- Option<std::string> string;
- Option<U32> flags;
+ Alternative<std::string> string;
+ Alternative<U32> flags;
- Follows()
- : string(""),
- flags("flags", FOLLOWS_LEFT | FOLLOWS_TOP)
- {}
+ Follows();
};
struct Params : public LLInitParam::Block<Params>
@@ -157,22 +113,18 @@ public:
Mandatory<std::string> name;
Optional<bool> enabled,
- visible;
- Optional<bool> mouse_opaque;
- Optional<bool> use_bounding_rect;
+ visible,
+ mouse_opaque,
+ use_bounding_rect,
+ from_xui;
+
Optional<S32> tab_group,
default_tab_group;
Optional<std::string> tool_tip;
Optional<S32> sound_flags;
- Optional<bool> serializable;
Optional<Follows> follows;
Optional<std::string> hover_cursor;
-
- // font params
- Optional<const LLFontGL*> font;
- Optional<LLFontGL::HAlign> font_halign;
- Optional<LLFontGL::VAlign> font_valign;
Optional<std::string> layout;
Optional<LLRect> rect;
@@ -185,27 +137,36 @@ public:
left_pad, // from last right to my left
left_delta; // from last left to my left
- Optional<bool> center_horiz,
- center_vert;
-
// these are nested attributes for LLLayoutPanel
//FIXME: get parent context involved in parsing traversal
- Deprecated user_resize,
+ Ignored user_resize,
auto_resize,
- needs_translate;
+ needs_translate,
+ min_width,
+ max_width,
+ xmlns,
+ xmlns_xsi,
+ xsi_schemaLocation,
+ xsi_type;
Params();
};
+
+ typedef LLViewWidgetRegistry child_registry_t;
+
void initFromParams(const LLView::Params&);
protected:
LLView(const LLView::Params&);
friend class LLUICtrlFactory;
+private:
+ // widgets in general are not copyable
+ LLView(const LLView& other) {};
public:
-#if LL_DEBUG
+//#if LL_DEBUG
static BOOL sIsDrawing;
-#endif
+//#endif
enum ESoundFlags
{
SILENT = 0,
@@ -247,9 +208,6 @@ public:
virtual ~LLView();
- // Hack to support LLFocusMgr (from LLMouseHandler)
- /*virtual*/ BOOL isView() const;
-
// Some UI widgets need to be added as controls. Others need to
// be added as regular view children. isCtrl should return TRUE
// if a widget needs to be added as a ctrl
@@ -282,6 +240,8 @@ public:
void setUseBoundingRect( BOOL use_bounding_rect );
BOOL getUseBoundingRect();
+ ECursorType getHoverCursor() { return mHoverCursor; }
+
const std::string& getToolTip() const { return mToolTipMsg.getString(); }
void sendChildToFront(LLView* child);
@@ -297,12 +257,9 @@ public:
// remove the specified child from the view, and set it's parent to NULL.
virtual void removeChild(LLView* view);
- // helper function for lluictrlfactory.h create<> template
- void setParent(LLView* parent) { if (parent) parent->addChild(this); }
-
virtual BOOL postBuild() { return TRUE; }
- child_tab_order_t getCtrlOrder() const { return mCtrlOrder; }
+ const child_tab_order_t& getCtrlOrder() const { return mCtrlOrder; }
ctrl_list_t getCtrlList() const;
ctrl_list_t getCtrlListSorted() const;
@@ -325,23 +282,27 @@ public:
// children, etc.
virtual void deleteAllChildren();
- virtual void setTentative(BOOL b);
- virtual BOOL getTentative() const;
void setAllChildrenEnabled(BOOL b);
virtual void setVisible(BOOL visible);
BOOL getVisible() const { return mVisible; }
virtual void setEnabled(BOOL enabled);
BOOL getEnabled() const { return mEnabled; }
+ /// 'available' in this context means 'visible and enabled': in other
+ /// words, can a user actually interact with this?
+ virtual bool isAvailable() const;
+ /// The static isAvailable() tests an LLView* that could be NULL.
+ static bool isAvailable(const LLView* view);
U8 getSoundFlags() const { return mSoundFlags; }
virtual BOOL setLabelArg( const std::string& key, const LLStringExplicit& text );
- virtual void onVisibilityChange ( BOOL curVisibilityIn );
+ virtual void handleVisibilityChange ( BOOL new_visibility );
void pushVisible(BOOL visible) { mLastVisible = mVisible; setVisible(visible); }
void popVisible() { setVisible(mLastVisible); }
-
+ BOOL getLastVisible() const { return mLastVisible; }
+
LLHandle<LLView> getHandle() { mHandle.bind(this); return mHandle; }
U32 getFollows() const { return mReshapeFlags; }
@@ -364,6 +325,7 @@ public:
// Override and return required size for this object. 0 for width/height means don't care.
virtual LLRect getRequiredRect();
+ LLRect calcBoundingRect();
void updateBoundingRect();
LLView* getRootView();
@@ -377,9 +339,23 @@ public:
BOOL hasChild(const std::string& childname, BOOL recurse = FALSE) const;
BOOL childHasKeyboardFocus( const std::string& childname ) const;
+ // these iterators are used for collapsing various tree traversals into for loops
typedef LLTreeDFSIter<LLView, child_list_const_iter_t> tree_iterator_t;
- tree_iterator_t beginTree();
- tree_iterator_t endTree();
+ tree_iterator_t beginTreeDFS();
+ tree_iterator_t endTreeDFS();
+
+ typedef LLTreeDFSPostIter<LLView, child_list_const_iter_t> tree_post_iterator_t;
+ tree_post_iterator_t beginTreeDFSPost();
+ tree_post_iterator_t endTreeDFSPost();
+
+ typedef LLTreeBFSIter<LLView, child_list_const_iter_t> bfs_tree_iterator_t;
+ bfs_tree_iterator_t beginTreeBFS();
+ bfs_tree_iterator_t endTreeBFS();
+
+
+ typedef LLTreeDownIter<LLView> root_to_view_iterator_t;
+ root_to_view_iterator_t beginRootToView();
+ root_to_view_iterator_t endRootToView();
//
// UTILITIES
@@ -390,6 +366,7 @@ public:
virtual void translate( S32 x, S32 y );
void setOrigin( S32 x, S32 y ) { mRect.translate( x - mRect.mLeft, y - mRect.mBottom ); }
BOOL translateIntoRect( const LLRect& constraint, BOOL allow_partial_outside );
+ BOOL translateIntoRectWithExclusion( const LLRect& inside, const LLRect& exclude, BOOL allow_partial_outside );
void centerWithin(const LLRect& bounds);
void setShape(const LLRect& new_rect, bool by_user = false);
@@ -398,26 +375,23 @@ public:
virtual BOOL canSnapTo(const LLView* other_view);
virtual void setSnappedTo(const LLView* snap_view);
- virtual BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent);
- virtual BOOL handleUnicodeChar(llwchar uni_char, BOOL called_from_parent);
+ // inherited from LLFocusableElement
+ /* virtual */ BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent);
+ /* virtual */ BOOL handleUnicodeChar(llwchar uni_char, BOOL called_from_parent);
+
virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
EDragAndDropType cargo_type,
void* cargo_data,
EAcceptance* accept,
std::string& tooltip_msg);
- virtual std::string getShowNamesToolTip();
-
virtual void draw();
void parseFollowsFlags(const LLView::Params& params);
// Some widgets, like close box buttons, don't need to be saved
- BOOL getSaveToXML() const { return mSaveToXML; }
- void setSaveToXML(BOOL b) { mSaveToXML = b; }
-
- virtual void onFocusLost();
- virtual void onFocusReceived();
+ BOOL getFromXUI() const { return mFromXUI; }
+ void setFromXUI(BOOL b) { mFromXUI = b; }
typedef enum e_hit_test_type
{
@@ -457,14 +431,16 @@ public:
/*virtual*/ BOOL handleScrollWheel(S32 x, S32 y, S32 clicks);
/*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask);
/*virtual*/ BOOL handleRightMouseUp(S32 x, S32 y, MASK mask);
- /*virtual*/ BOOL handleToolTip(S32 x, S32 y, std::string& msg, LLRect* sticky_rect); // Display mToolTipMsg if no child handles it.
- /*virtual*/ const std::string& getName() const;
+ /*virtual*/ BOOL handleToolTip(S32 x, S32 y, MASK mask);
+
+ /*virtual*/ std::string getName() const;
/*virtual*/ void onMouseCaptureLost();
/*virtual*/ BOOL hasMouseCapture();
- /*virtual*/ BOOL isView(); // Hack to support LLFocusMgr
/*virtual*/ void screenPointToLocal(S32 screen_x, S32 screen_y, S32* local_x, S32* local_y) const;
/*virtual*/ void localPointToScreen(S32 local_x, S32 local_y, S32* screen_x, S32* screen_y) const;
+ virtual LLView* childFromPoint(S32 x, S32 y);
+
// view-specific handlers
virtual void onMouseEnter(S32 x, S32 y, MASK mask);
virtual void onMouseLeave(S32 x, S32 y, MASK mask);
@@ -472,33 +448,31 @@ public:
template <class T> T* findChild(const std::string& name, BOOL recurse = TRUE) const
{
- LLView* child = getChildView(name, recurse, FALSE);
+ LLView* child = findChildView(name, recurse);
T* result = dynamic_cast<T*>(child);
return result;
}
- template <class T> T* getChild(const std::string& name, BOOL recurse = TRUE, BOOL create_if_missing = TRUE) const;
+ template <class T> T* getChild(const std::string& name, BOOL recurse = TRUE) const;
template <class T> T& getChildRef(const std::string& name, BOOL recurse = TRUE) const
{
- return *getChild<T>(name, recurse, TRUE);
+ return *getChild<T>(name, recurse);
}
- virtual LLView* getChildView(const std::string& name, BOOL recurse = TRUE, BOOL create_if_missing = TRUE) const;
+ virtual LLView* getChildView(const std::string& name, BOOL recurse = TRUE) const;
+ virtual LLView* findChildView(const std::string& name, BOOL recurse = TRUE) const;
- template <class T> T* getDummyWidget(const std::string& name) const
+ template <class T> T* getDefaultWidget(const std::string& name) const
{
- dummy_widget_map_t::const_iterator found_it = getDummyWidgetMap().find(name);
- if (found_it == getDummyWidgetMap().end())
+ default_widget_map_t::const_iterator found_it = getDefaultWidgetMap().find(name);
+ if (found_it == getDefaultWidgetMap().end())
{
return NULL;
}
return dynamic_cast<T*>(found_it->second);
}
- // determines allowable children when parsing XUI
- virtual const widget_registry_t& getChildRegistry() const;
-
//////////////////////////////////////////////
// statics
//////////////////////////////////////////////
@@ -519,7 +493,7 @@ public:
// Set up params after XML load before calling new(),
// usually to adjust layout.
- static void setupParams(Params& p, LLView* parent);
+ static void applyXUILayout(Params& p, LLView* parent);
// For re-export of floaters and panels, convert the coordinate system
// to be top-left based.
@@ -530,10 +504,24 @@ public:
virtual BOOL handleUnicodeCharHere(llwchar uni_char);
virtual void handleReshape(const LLRect& rect, bool by_user);
+ virtual void dirtyRect();
+
+ //send custom notification to LLView parent
+ virtual S32 notifyParent(const LLSD& info);
+
+ //send custom notification to all view childrend
+ // return true if _any_ children return true. otherwise false.
+ virtual bool notifyChildren(const LLSD& info);
+
+ //send custom notification to current view
+ virtual S32 notify(const LLSD& info) { return 0;};
+
+ static const LLViewDrawContext& getDrawContext();
protected:
void drawDebugRect();
void drawChild(LLView* childp, S32 x_offset = 0, S32 y_offset = 0, BOOL force_draw = FALSE);
+ void drawChildren();
LLView* childrenHandleKey(KEY key, MASK mask);
LLView* childrenHandleUnicodeChar(llwchar uni_char);
@@ -553,10 +541,12 @@ protected:
LLView* childrenHandleScrollWheel(S32 x, S32 y, S32 clicks);
LLView* childrenHandleRightMouseDown(S32 x, S32 y, MASK mask);
LLView* childrenHandleRightMouseUp(S32 x, S32 y, MASK mask);
+ LLView* childrenHandleToolTip(S32 x, S32 y, MASK mask);
ECursorType mHoverCursor;
private:
+
LLView* mParentView;
child_list_t mChildList;
@@ -578,7 +568,7 @@ private:
LLUIString mToolTipMsg; // isNull() is true if none.
U8 mSoundFlags;
- BOOL mSaveToXML;
+ BOOL mFromXUI;
BOOL mIsFocusRoot;
BOOL mUseBoundingRect; // hit test against bounding rectangle that includes all child elements
@@ -592,21 +582,27 @@ private:
static LLWindow* sWindow; // All root views must know about their window.
- typedef std::map<std::string, LLView*> dummy_widget_map_t;
+ typedef std::map<std::string, LLView*> default_widget_map_t;
// allocate this map no demand, as it is rarely needed
- mutable dummy_widget_map_t* mDummyWidgets;
+ mutable default_widget_map_t* mDefaultWidgets;
- dummy_widget_map_t& getDummyWidgetMap() const;
+ default_widget_map_t& getDefaultWidgetMap() const;
public:
- static BOOL sDebugRects; // Draw debug rects behind everything.
- static BOOL sDebugKeys;
+ // Depth in view hierarchy during rendering
static S32 sDepth;
- static BOOL sDebugMouseHandling;
+
+ // Draw debug rectangles around widgets to help with alignment and spacing
+ static bool sDebugRects;
+
+ // Draw widget names and sizes when drawing debug rectangles, turning this
+ // off is useful to make the rectangles themselves easier to see.
+ static bool sDebugRectsShowNames;
+
+ static bool sDebugKeys;
+ static bool sDebugMouseHandling;
static std::string sMouseHandlerMessage;
static S32 sSelectID;
-// static BOOL sEditingUI;
-// static LLView* sEditingUIView;
static std::set<LLView*> sPreviewHighlightedElements; // DEV-16869
static BOOL sHighlightingDiffs; // DEV-16869
static LLView* sPreviewClickedElement; // DEV-16869
@@ -619,47 +615,55 @@ public:
class LLCompareByTabOrder
{
public:
- LLCompareByTabOrder(LLView::child_tab_order_t order) : mTabOrder(order) {}
+ LLCompareByTabOrder(const LLView::child_tab_order_t& order) : mTabOrder(order) {}
virtual ~LLCompareByTabOrder() {}
bool operator() (const LLView* const a, const LLView* const b) const;
private:
virtual bool compareTabOrders(const LLView::tab_order_t & a, const LLView::tab_order_t & b) const { return a < b; }
- LLView::child_tab_order_t mTabOrder;
+ // ok to store a reference, as this should only be allocated on stack during view query operations
+ const LLView::child_tab_order_t& mTabOrder;
};
-template <class T> T* LLView::getChild(const std::string& name, BOOL recurse, BOOL create_if_missing) const
+template <class T> T* LLView::getChild(const std::string& name, BOOL recurse) const
{
- LLView* child = getChildView(name, recurse, FALSE);
+ LLView* child = findChildView(name, recurse);
T* result = dynamic_cast<T*>(child);
if (!result)
{
// did we find *something* with that name?
if (child)
{
- llwarns << "Found child named " << name << " but of wrong type " << typeid(child).name() << ", expecting " << typeid(T*).name() << llendl;
+ llwarns << "Found child named \"" << name << "\" but of wrong type " << typeid(*child).name() << ", expecting " << typeid(T*).name() << llendl;
}
- if (create_if_missing)
+ result = getDefaultWidget<T>(name);
+ if (!result)
{
- result = getDummyWidget<T>(name);
- if (!result)
+ result = LLUICtrlFactory::getDefaultWidget<T>(name);
+
+ if (result)
+ {
+ // *NOTE: You cannot call mFoo = getChild<LLFoo>("bar")
+ // in a floater or panel constructor. The widgets will not
+ // be ready. Instead, put it in postBuild().
+ llwarns << "Making dummy " << typeid(T).name() << " named \"" << name << "\" in " << getName() << llendl;
+ }
+ else
{
- result = LLUICtrlFactory::createDummyWidget<T>(name);
-
- if (result)
- {
- llwarns << "Making dummy " << typeid(T).name() << " named \"" << name << "\" in " << getName() << llendl;
- }
- else
- {
- llwarns << "Failed to create dummy " << typeid(T).name() << llendl;
- return NULL;
- }
-
- getDummyWidgetMap()[name] = result;
+ llwarns << "Failed to create dummy " << typeid(T).name() << llendl;
+ return NULL;
}
+
+ getDefaultWidgetMap()[name] = result;
}
}
return result;
}
+// Compiler optimization - don't generate these specializations inline,
+// require explicit specialization. See llbutton.cpp for an example.
+#ifndef LLVIEW_CPP
+extern template class LLView* LLView::getChild<class LLView>(
+ const std::string& name, BOOL recurse) const;
+#endif
+
#endif //LL_LLVIEW_H
diff --git a/indra/llui/llviewborder.cpp b/indra/llui/llviewborder.cpp
index a5b09671bb..89cd34c37c 100644
--- a/indra/llui/llviewborder.cpp
+++ b/indra/llui/llviewborder.cpp
@@ -1,31 +1,25 @@
/**
* @file llviewborder.cpp
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -35,7 +29,7 @@
#include "llfocusmgr.h"
#include "lluictrlfactory.h"
-static LLDefaultWidgetRegistry::Register<LLViewBorder> r("view_border");
+static LLDefaultChildRegistry::Register<LLViewBorder> r("view_border");
void LLViewBorder::BevelValues::declareValues()
{
@@ -52,7 +46,7 @@ void LLViewBorder::StyleValues::declareValues()
}
LLViewBorder::Params::Params()
-: bevel_type("bevel_style", BEVEL_OUT),
+: bevel_style("bevel_style", BEVEL_OUT),
render_style("border_style", STYLE_LINE),
border_thickness("border_thickness"),
highlight_light_color("highlight_light_color"),
@@ -60,6 +54,8 @@ LLViewBorder::Params::Params()
shadow_light_color("shadow_light_color"),
shadow_dark_color("shadow_dark_color")
{
+ addSynonym(border_thickness, "thickness");
+ addSynonym(render_style, "style");
name = "view_border";
mouse_opaque = false;
follows.flags = FOLLOWS_ALL;
@@ -75,7 +71,7 @@ LLViewBorder::LLViewBorder(const LLViewBorder::Params& p)
mHighlightDark(p.highlight_dark_color()),
mShadowLight(p.shadow_light_color()),
mShadowDark(p.shadow_dark_color()),
- mBevel(p.bevel_type),
+ mBevel(p.bevel_style),
mStyle(p.render_style)
{}
@@ -123,18 +119,8 @@ void LLViewBorder::draw()
llassert( FALSE ); // not implemented
}
}
- else
- if( STYLE_TEXTURE == mStyle )
- {
- if( mTexture )
- {
- drawTextures();
- }
- }
- // draw the children
LLView::draw();
-
}
void LLViewBorder::drawOnePixelLines()
@@ -255,56 +241,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 37e13fb181..413ce39744 100644
--- a/indra/llui/llviewborder.h
+++ b/indra/llui/llviewborder.h
@@ -2,31 +2,25 @@
* @file llviewborder.h
* @brief A customizable decorative border. Does not interact with mouse events.
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -55,7 +49,7 @@ public:
struct Params : public LLInitParam::Block<Params, LLView::Params>
{
- Optional<EBevel, BevelValues> bevel_type;
+ Optional<EBevel, BevelValues> bevel_style;
Optional<EStyle, StyleValues> render_style;
Optional<S32> border_thickness;
@@ -99,8 +93,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.cpp b/indra/llui/llviewmodel.cpp
index 4107289e85..a9f8acc440 100644
--- a/indra/llui/llviewmodel.cpp
+++ b/indra/llui/llviewmodel.cpp
@@ -4,31 +4,25 @@
* @date 2008-08-08
* @brief Implementation for llviewmodel.
*
- * $LicenseInfo:firstyear=2008&license=viewergpl$
- *
- * Copyright (c) 2008-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2008&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llui/llviewmodel.h b/indra/llui/llviewmodel.h
index c8a9b52cca..763af5d8a2 100644
--- a/indra/llui/llviewmodel.h
+++ b/indra/llui/llviewmodel.h
@@ -9,31 +9,25 @@
* than the business "model" object underlying the overall "view"
* presented by the collection of widgets.
*
- * $LicenseInfo:firstyear=2008&license=viewergpl$
- *
- * Copyright (c) 2008-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2008&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -107,7 +101,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/llviewquery.cpp b/indra/llui/llviewquery.cpp
index bdb3d223a6..d0b78ba186 100644
--- a/indra/llui/llviewquery.cpp
+++ b/indra/llui/llviewquery.cpp
@@ -2,31 +2,25 @@
* @file llviewquery.cpp
* @brief Implementation of view query class.
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -95,8 +89,8 @@ viewList_t LLViewQuery::run(LLView* view) const
if (pre.first)
{
post = runFilters(view, filtered_children, mPostFilters);
+ }
}
- }
if(pre.first && post.first)
{
@@ -119,12 +113,12 @@ void LLViewQuery::filterChildren(LLView * view, viewList_t & filtered_children)
(*mSorterp)(view, views); // sort the children per the sorter
}
for(LLView::child_list_iter_t iter = views.begin();
- iter != views.end();
- iter++)
- {
- viewList_t indiv_children = this->run(*iter);
- filtered_children.insert(filtered_children.end(), indiv_children.begin(), indiv_children.end());
- }
+ iter != views.end();
+ iter++)
+ {
+ viewList_t indiv_children = this->run(*iter);
+ filtered_children.splice(filtered_children.end(), indiv_children);
+ }
}
filterResult_t LLViewQuery::runFilters(LLView * view, const viewList_t children, const filterList_t filters) const
diff --git a/indra/llui/llviewquery.h b/indra/llui/llviewquery.h
index 98d9bf8796..210f95162a 100644
--- a/indra/llui/llviewquery.h
+++ b/indra/llui/llviewquery.h
@@ -2,31 +2,25 @@
* @file llviewquery.h
* @brief Query algorithm for flattening and filtering the view hierarchy.
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -122,7 +116,7 @@ public:
viewList_t operator () (LLView * view) const { return run(view); }
// override this method to provide iteration over other types of children
- virtual void filterChildren(LLView * view, viewList_t & filtered_children) const;
+ virtual void filterChildren(LLView * view, viewList_t& filtered_children) const;
private:
diff --git a/indra/llui/tests/llurlentry_stub.cpp b/indra/llui/tests/llurlentry_stub.cpp
new file mode 100644
index 0000000000..ff53ae5624
--- /dev/null
+++ b/indra/llui/tests/llurlentry_stub.cpp
@@ -0,0 +1,69 @@
+/**
+ * @file llurlentry_stub.cpp
+ * @author Martin Reddy
+ * @brief Stub implementations for LLUrlEntry unit test dependencies
+ *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "llstring.h"
+#include "llfile.h"
+#include "llcachename.h"
+#include "lluuid.h"
+
+#include <string>
+
+//
+// Stub implementation for LLCacheName
+//
+BOOL LLCacheName::getFullName(const LLUUID& id, std::string& fullname)
+{
+ fullname = "Lynx Linden";
+ return TRUE;
+}
+
+BOOL LLCacheName::getGroupName(const LLUUID& id, std::string& group)
+{
+ group = "My Group";
+ return TRUE;
+}
+
+boost::signals2::connection LLCacheName::get(const LLUUID& id, BOOL is_group, const LLCacheNameCallback& callback)
+{
+ return boost::signals2::connection();
+}
+
+LLCacheName* gCacheName = NULL;
+
+//
+// Stub implementation for LLTrans
+//
+class LLTrans
+{
+public:
+ static std::string getString(const std::string &xml_desc, const LLStringUtil::format_map_t& args);
+};
+
+std::string LLTrans::getString(const std::string &xml_desc, const LLStringUtil::format_map_t& args)
+{
+ return std::string();
+}
diff --git a/indra/llui/tests/llurlentry_test.cpp b/indra/llui/tests/llurlentry_test.cpp
new file mode 100644
index 0000000000..95affe4460
--- /dev/null
+++ b/indra/llui/tests/llurlentry_test.cpp
@@ -0,0 +1,670 @@
+/**
+ * @file llurlentry_test.cpp
+ * @author Martin Reddy
+ * @brief Unit tests for LLUrlEntry objects
+ *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+#include "../llurlentry.h"
+#include "llurlentry_stub.cpp"
+#include "lltut.h"
+#include "../lluicolortable.h"
+
+#include <boost/regex.hpp>
+
+LLUIColor LLUIColorTable::getColor(const std::string& name, const LLColor4& default_color) const
+{
+ return LLUIColor();
+}
+
+LLUIColor::LLUIColor() : mColorPtr(NULL) {}
+
+namespace tut
+{
+ struct LLUrlEntryData
+ {
+ };
+
+ typedef test_group<LLUrlEntryData> factory;
+ typedef factory::object object;
+}
+
+namespace
+{
+ tut::factory tf("LLUrlEntry");
+}
+
+namespace tut
+{
+ void testRegex(const std::string &testname, LLUrlEntryBase &entry,
+ const char *text, const std::string &expected)
+ {
+ boost::regex regex = entry.getPattern();
+ std::string url = "";
+ boost::cmatch result;
+ bool found = boost::regex_search(text, result, regex);
+ if (found)
+ {
+ S32 start = static_cast<U32>(result[0].first - text);
+ S32 end = static_cast<U32>(result[0].second - text);
+ url = entry.getUrl(std::string(text+start, end-start));
+ }
+ ensure_equals(testname, url, expected);
+ }
+
+ template<> template<>
+ void object::test<1>()
+ {
+ //
+ // test LLUrlEntryHTTP - standard http Urls
+ //
+ LLUrlEntryHTTP url;
+
+ testRegex("no valid url", url,
+ "htp://slurl.com/",
+ "");
+
+ testRegex("simple http (1)", url,
+ "http://slurl.com/",
+ "http://slurl.com/");
+
+ testRegex("simple http (2)", url,
+ "http://slurl.com",
+ "http://slurl.com");
+
+ testRegex("simple http (3)", url,
+ "http://slurl.com/about.php",
+ "http://slurl.com/about.php");
+
+ testRegex("simple https", url,
+ "https://slurl.com/about.php",
+ "https://slurl.com/about.php");
+
+ testRegex("http in text (1)", url,
+ "XX http://slurl.com/ XX",
+ "http://slurl.com/");
+
+ testRegex("http in text (2)", url,
+ "XX http://slurl.com/about.php XX",
+ "http://slurl.com/about.php");
+
+ testRegex("https in text", url,
+ "XX https://slurl.com/about.php XX",
+ "https://slurl.com/about.php");
+
+ testRegex("two http urls", url,
+ "XX http://slurl.com/about.php http://secondlife.com/ XX",
+ "http://slurl.com/about.php");
+
+ testRegex("http url with port and username", url,
+ "XX http://nobody@slurl.com:80/about.php http://secondlife.com/ XX",
+ "http://nobody@slurl.com:80/about.php");
+
+ testRegex("http url with port, username, and query string", url,
+ "XX http://nobody@slurl.com:80/about.php?title=hi%20there http://secondlife.com/ XX",
+ "http://nobody@slurl.com:80/about.php?title=hi%20there");
+
+ // note: terminating commas will be removed by LLUrlRegistry:findUrl()
+ testRegex("http url with commas in middle and terminating", url,
+ "XX http://slurl.com/?title=Hi,There, XX",
+ "http://slurl.com/?title=Hi,There,");
+
+ // note: terminating periods will be removed by LLUrlRegistry:findUrl()
+ testRegex("http url with periods in middle and terminating", url,
+ "XX http://slurl.com/index.php. XX",
+ "http://slurl.com/index.php.");
+
+ // DEV-19842: Closing parenthesis ")" breaks urls
+ testRegex("http url with brackets (1)", url,
+ "XX http://en.wikipedia.org/wiki/JIRA_(software) XX",
+ "http://en.wikipedia.org/wiki/JIRA_(software)");
+
+ // DEV-19842: Closing parenthesis ")" breaks urls
+ testRegex("http url with brackets (2)", url,
+ "XX http://jira.secondlife.com/secure/attachment/17990/eggy+avs+in+1.21.0+(93713)+public+nightly.jpg XX",
+ "http://jira.secondlife.com/secure/attachment/17990/eggy+avs+in+1.21.0+(93713)+public+nightly.jpg");
+
+ // DEV-10353: URLs in chat log terminated incorrectly when newline in chat
+ testRegex("http url with newlines", url,
+ "XX\nhttp://www.secondlife.com/\nXX",
+ "http://www.secondlife.com/");
+ }
+
+ template<> template<>
+ void object::test<2>()
+ {
+ //
+ // test LLUrlEntryHTTPLabel - wiki-style http Urls with labels
+ //
+ LLUrlEntryHTTPLabel url;
+
+ testRegex("invalid wiki url [1]", url,
+ "[http://www.example.org]",
+ "");
+
+ testRegex("invalid wiki url [2]", url,
+ "[http://www.example.org",
+ "");
+
+ testRegex("invalid wiki url [3]", url,
+ "[http://www.example.org Label",
+ "");
+
+ testRegex("example.org with label (spaces)", url,
+ "[http://www.example.org Text]",
+ "http://www.example.org");
+
+ testRegex("example.org with label (tabs)", url,
+ "[http://www.example.org\t Text]",
+ "http://www.example.org");
+
+ testRegex("SL http URL with label", url,
+ "[http://www.secondlife.com/ Second Life]",
+ "http://www.secondlife.com/");
+
+ testRegex("SL https URL with label", url,
+ "XXX [https://www.secondlife.com/ Second Life] YYY",
+ "https://www.secondlife.com/");
+
+ testRegex("SL http URL with label", url,
+ "[http://www.secondlife.com/?test=Hi%20There Second Life]",
+ "http://www.secondlife.com/?test=Hi%20There");
+ }
+
+ template<> template<>
+ void object::test<3>()
+ {
+ //
+ // test LLUrlEntrySLURL - second life URLs
+ //
+ LLUrlEntrySLURL url;
+
+ testRegex("no valid slurl [1]", url,
+ "htp://slurl.com/secondlife/Ahern/50/50/50/",
+ "");
+
+ testRegex("no valid slurl [2]", url,
+ "http://slurl.com/secondlife/",
+ "");
+
+ testRegex("no valid slurl [3]", url,
+ "hhtp://slurl.com/secondlife/Ahern/50/FOO/50/",
+ "");
+
+ testRegex("Ahern (50,50,50) [1]", url,
+ "http://slurl.com/secondlife/Ahern/50/50/50/",
+ "http://slurl.com/secondlife/Ahern/50/50/50/");
+
+ testRegex("Ahern (50,50,50) [2]", url,
+ "XXX http://slurl.com/secondlife/Ahern/50/50/50/ XXX",
+ "http://slurl.com/secondlife/Ahern/50/50/50/");
+
+ testRegex("Ahern (50,50,50) [3]", url,
+ "XXX http://slurl.com/secondlife/Ahern/50/50/50 XXX",
+ "http://slurl.com/secondlife/Ahern/50/50/50");
+
+ testRegex("Ahern (50,50,50) multicase", url,
+ "XXX http://SLUrl.com/SecondLife/Ahern/50/50/50/ XXX",
+ "http://SLUrl.com/SecondLife/Ahern/50/50/50/");
+
+ testRegex("Ahern (50,50) [1]", url,
+ "XXX http://slurl.com/secondlife/Ahern/50/50/ XXX",
+ "http://slurl.com/secondlife/Ahern/50/50/");
+
+ testRegex("Ahern (50,50) [2]", url,
+ "XXX http://slurl.com/secondlife/Ahern/50/50 XXX",
+ "http://slurl.com/secondlife/Ahern/50/50");
+
+ testRegex("Ahern (50)", url,
+ "XXX http://slurl.com/secondlife/Ahern/50 XXX",
+ "http://slurl.com/secondlife/Ahern/50");
+
+ testRegex("Ahern", url,
+ "XXX http://slurl.com/secondlife/Ahern/ XXX",
+ "http://slurl.com/secondlife/Ahern/");
+
+ testRegex("Ahern SLURL with title", url,
+ "XXX http://slurl.com/secondlife/Ahern/50/50/50/?title=YOUR%20TITLE%20HERE! XXX",
+ "http://slurl.com/secondlife/Ahern/50/50/50/?title=YOUR%20TITLE%20HERE!");
+
+ testRegex("Ahern SLURL with msg", url,
+ "XXX http://slurl.com/secondlife/Ahern/50/50/50/?msg=Your%20text%20here. XXX",
+ "http://slurl.com/secondlife/Ahern/50/50/50/?msg=Your%20text%20here.");
+
+ // DEV-21577: In-world SLURLs containing "(" or ")" are not treated as a hyperlink in chat
+ testRegex("SLURL with brackets", url,
+ "XXX http://slurl.com/secondlife/Burning%20Life%20(Hyper)/27/210/30 XXX",
+ "http://slurl.com/secondlife/Burning%20Life%20(Hyper)/27/210/30");
+
+ // DEV-35459: SLURLs and teleport Links not parsed properly
+ testRegex("SLURL with quote", url,
+ "XXX http://slurl.com/secondlife/A'ksha%20Oasis/41/166/701 XXX",
+ "http://slurl.com/secondlife/A%27ksha%20Oasis/41/166/701");
+ }
+
+ template<> template<>
+ void object::test<4>()
+ {
+ //
+ // test LLUrlEntryAgent - secondlife://app/agent Urls
+ //
+ LLUrlEntryAgent url;
+
+ testRegex("Invalid Agent Url", url,
+ "secondlife:///app/agent/0e346d8b-4433-4d66-XXXX-fd37083abc4c/about",
+ "");
+
+ testRegex("Agent Url ", url,
+ "secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about",
+ "secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about");
+
+ testRegex("Agent Url in text", url,
+ "XXX secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about XXX",
+ "secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about");
+
+ testRegex("Agent Url multicase", url,
+ "XXX secondlife:///App/AGENT/0E346D8B-4433-4d66-a6b0-fd37083abc4c/About XXX",
+ "secondlife:///App/AGENT/0E346D8B-4433-4d66-a6b0-fd37083abc4c/About");
+
+ testRegex("Agent Url alternate command", url,
+ "XXX secondlife:///App/AGENT/0E346D8B-4433-4d66-a6b0-fd37083abc4c/foobar",
+ "secondlife:///App/AGENT/0E346D8B-4433-4d66-a6b0-fd37083abc4c/foobar");
+
+ testRegex("Standalone Agent Url ", url,
+ "x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about",
+ "x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about");
+
+ testRegex("Standalone Agent Url Multicase with Text", url,
+ "M x-grid-location-info://lincoln.lindenlab.com/app/AGENT/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about M",
+ "x-grid-location-info://lincoln.lindenlab.com/app/AGENT/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about");
+ }
+
+ template<> template<>
+ void object::test<5>()
+ {
+ //
+ // test LLUrlEntryGroup - secondlife://app/group Urls
+ //
+ LLUrlEntryGroup url;
+
+ testRegex("Invalid Group Url", url,
+ "secondlife:///app/group/00005ff3-4044-c79f-XXXX-fb28ae0df991/about",
+ "");
+
+ testRegex("Group Url ", url,
+ "secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/about",
+ "secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/about");
+
+ testRegex("Group Url ", url,
+ "secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/inspect",
+ "secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/inspect");
+
+ testRegex("Group Url in text", url,
+ "XXX secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/about XXX",
+ "secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/about");
+
+ testRegex("Group Url multicase", url,
+ "XXX secondlife:///APP/Group/00005FF3-4044-c79f-9de8-fb28ae0df991/About XXX",
+ "secondlife:///APP/Group/00005FF3-4044-c79f-9de8-fb28ae0df991/About");
+
+ testRegex("Standalone Group Url ", url,
+ "x-grid-location-info://lincoln.lindenlab.com/app/group/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about",
+ "x-grid-location-info://lincoln.lindenlab.com/app/group/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about");
+
+ testRegex("Standalone Group Url Multicase ith Text", url,
+ "M x-grid-location-info://lincoln.lindenlab.com/app/GROUP/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about M",
+ "x-grid-location-info://lincoln.lindenlab.com/app/GROUP/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about");
+
+ }
+
+ template<> template<>
+ void object::test<6>()
+ {
+ //
+ // test LLUrlEntryPlace - secondlife://<location> URLs
+ //
+ LLUrlEntryPlace url;
+
+ testRegex("no valid slurl [1]", url,
+ "secondlife://Ahern/FOO/50/",
+ "");
+
+ testRegex("Ahern (50,50,50) [1]", url,
+ "secondlife://Ahern/50/50/50/",
+ "secondlife://Ahern/50/50/50/");
+
+ testRegex("Ahern (50,50,50) [2]", url,
+ "XXX secondlife://Ahern/50/50/50/ XXX",
+ "secondlife://Ahern/50/50/50/");
+
+ testRegex("Ahern (50,50,50) [3]", url,
+ "XXX secondlife://Ahern/50/50/50 XXX",
+ "secondlife://Ahern/50/50/50");
+
+ testRegex("Ahern (50,50,50) multicase", url,
+ "XXX SecondLife://Ahern/50/50/50/ XXX",
+ "SecondLife://Ahern/50/50/50/");
+
+ testRegex("Ahern (50,50) [1]", url,
+ "XXX secondlife://Ahern/50/50/ XXX",
+ "secondlife://Ahern/50/50/");
+
+ testRegex("Ahern (50,50) [2]", url,
+ "XXX secondlife://Ahern/50/50 XXX",
+ "secondlife://Ahern/50/50");
+
+ // DEV-21577: In-world SLURLs containing "(" or ")" are not treated as a hyperlink in chat
+ testRegex("SLURL with brackets", url,
+ "XXX secondlife://Burning%20Life%20(Hyper)/27/210/30 XXX",
+ "secondlife://Burning%20Life%20(Hyper)/27/210/30");
+
+ // DEV-35459: SLURLs and teleport Links not parsed properly
+ testRegex("SLURL with quote", url,
+ "XXX secondlife://A'ksha%20Oasis/41/166/701 XXX",
+ "secondlife://A%27ksha%20Oasis/41/166/701");
+
+ testRegex("Standalone All Hands (50,50) [2] with text", url,
+ "XXX x-grid-location-info://lincoln.lindenlab.com/region/All%20Hands/50/50/50 XXX",
+ "x-grid-location-info://lincoln.lindenlab.com/region/All%20Hands/50/50/50");
+ }
+
+ template<> template<>
+ void object::test<7>()
+ {
+ //
+ // test LLUrlEntryParcel - secondlife://app/parcel Urls
+ //
+ LLUrlEntryParcel url;
+
+ testRegex("Invalid Classified Url", url,
+ "secondlife:///app/parcel/0000060e-4b39-e00b-XXXX-d98b1934e3a8/about",
+ "");
+
+ testRegex("Classified Url ", url,
+ "secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about",
+ "secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about");
+
+ testRegex("Classified Url in text", url,
+ "XXX secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about XXX",
+ "secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about");
+
+ testRegex("Classified Url multicase", url,
+ "XXX secondlife:///APP/Parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/About XXX",
+ "secondlife:///APP/Parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/About");
+ }
+ template<> template<>
+ void object::test<8>()
+ {
+ //
+ // test LLUrlEntryTeleport - secondlife://app/teleport URLs
+ //
+ LLUrlEntryTeleport url;
+
+ testRegex("no valid teleport [1]", url,
+ "http://slurl.com/secondlife/Ahern/50/50/50/",
+ "");
+
+ testRegex("no valid teleport [2]", url,
+ "secondlife:///app/teleport/",
+ "");
+
+ testRegex("no valid teleport [3]", url,
+ "second-life:///app/teleport/Ahern/50/50/50/",
+ "");
+
+ testRegex("no valid teleport [3]", url,
+ "hhtp://slurl.com/secondlife/Ahern/50/FOO/50/",
+ "");
+
+ testRegex("Ahern (50,50,50) [1]", url,
+ "secondlife:///app/teleport/Ahern/50/50/50/",
+ "secondlife:///app/teleport/Ahern/50/50/50/");
+
+ testRegex("Ahern (50,50,50) [2]", url,
+ "XXX secondlife:///app/teleport/Ahern/50/50/50/ XXX",
+ "secondlife:///app/teleport/Ahern/50/50/50/");
+
+ testRegex("Ahern (50,50,50) [3]", url,
+ "XXX secondlife:///app/teleport/Ahern/50/50/50 XXX",
+ "secondlife:///app/teleport/Ahern/50/50/50");
+
+ testRegex("Ahern (50,50,50) multicase", url,
+ "XXX secondlife:///app/teleport/Ahern/50/50/50/ XXX",
+ "secondlife:///app/teleport/Ahern/50/50/50/");
+
+ testRegex("Ahern (50,50) [1]", url,
+ "XXX secondlife:///app/teleport/Ahern/50/50/ XXX",
+ "secondlife:///app/teleport/Ahern/50/50/");
+
+ testRegex("Ahern (50,50) [2]", url,
+ "XXX secondlife:///app/teleport/Ahern/50/50 XXX",
+ "secondlife:///app/teleport/Ahern/50/50");
+
+ testRegex("Ahern (50)", url,
+ "XXX secondlife:///app/teleport/Ahern/50 XXX",
+ "secondlife:///app/teleport/Ahern/50");
+
+ testRegex("Ahern", url,
+ "XXX secondlife:///app/teleport/Ahern/ XXX",
+ "secondlife:///app/teleport/Ahern/");
+
+ testRegex("Ahern teleport with title", url,
+ "XXX secondlife:///app/teleport/Ahern/50/50/50/?title=YOUR%20TITLE%20HERE! XXX",
+ "secondlife:///app/teleport/Ahern/50/50/50/?title=YOUR%20TITLE%20HERE!");
+
+ testRegex("Ahern teleport with msg", url,
+ "XXX secondlife:///app/teleport/Ahern/50/50/50/?msg=Your%20text%20here. XXX",
+ "secondlife:///app/teleport/Ahern/50/50/50/?msg=Your%20text%20here.");
+
+ // DEV-21577: In-world SLURLs containing "(" or ")" are not treated as a hyperlink in chat
+ testRegex("Teleport with brackets", url,
+ "XXX secondlife:///app/teleport/Burning%20Life%20(Hyper)/27/210/30 XXX",
+ "secondlife:///app/teleport/Burning%20Life%20(Hyper)/27/210/30");
+
+ // DEV-35459: SLURLs and teleport Links not parsed properly
+ testRegex("Teleport url with quote", url,
+ "XXX secondlife:///app/teleport/A'ksha%20Oasis/41/166/701 XXX",
+ "secondlife:///app/teleport/A%27ksha%20Oasis/41/166/701");
+
+ testRegex("Standalone All Hands", url,
+ "XXX x-grid-location-info://lincoln.lindenlab.com/app/teleport/All%20Hands/50/50/50 XXX",
+ "x-grid-location-info://lincoln.lindenlab.com/app/teleport/All%20Hands/50/50/50");
+ }
+
+ template<> template<>
+ void object::test<9>()
+ {
+ //
+ // test LLUrlEntrySL - general secondlife:// URLs
+ //
+ LLUrlEntrySL url;
+
+ testRegex("no valid slapp [1]", url,
+ "http:///app/",
+ "");
+
+ testRegex("valid slapp [1]", url,
+ "secondlife:///app/",
+ "secondlife:///app/");
+
+ testRegex("valid slapp [2]", url,
+ "secondlife:///app/teleport/Ahern/50/50/50/",
+ "secondlife:///app/teleport/Ahern/50/50/50/");
+
+ testRegex("valid slapp [3]", url,
+ "secondlife:///app/foo",
+ "secondlife:///app/foo");
+
+ testRegex("valid slapp [4]", url,
+ "secondlife:///APP/foo?title=Hi%20There",
+ "secondlife:///APP/foo?title=Hi%20There");
+
+ testRegex("valid slapp [5]", url,
+ "secondlife://host/app/",
+ "secondlife://host/app/");
+
+ testRegex("valid slapp [6]", url,
+ "secondlife://host:8080/foo/bar",
+ "secondlife://host:8080/foo/bar");
+ }
+
+ template<> template<>
+ void object::test<10>()
+ {
+ //
+ // test LLUrlEntrySLLabel - general secondlife:// URLs with labels
+ //
+ LLUrlEntrySLLabel url;
+
+ testRegex("invalid wiki url [1]", url,
+ "[secondlife:///app/]",
+ "");
+
+ testRegex("invalid wiki url [2]", url,
+ "[secondlife:///app/",
+ "");
+
+ testRegex("invalid wiki url [3]", url,
+ "[secondlife:///app/ Label",
+ "");
+
+ testRegex("agent slurl with label (spaces)", url,
+ "[secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about Text]",
+ "secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about");
+
+ testRegex("agent slurl with label (tabs)", url,
+ "[secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about\t Text]",
+ "secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about");
+
+ testRegex("agent slurl with label", url,
+ "[secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about FirstName LastName]",
+ "secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about");
+
+ testRegex("teleport slurl with label", url,
+ "XXX [secondlife:///app/teleport/Ahern/50/50/50/ Teleport to Ahern] YYY",
+ "secondlife:///app/teleport/Ahern/50/50/50/");
+ }
+
+ template<> template<>
+ void object::test<11>()
+ {
+ //
+ // test LLUrlEntryHTTPNoProtocol - general URLs without a protocol
+ //
+ LLUrlEntryHTTPNoProtocol url;
+
+ testRegex("naked .com URL", url,
+ "see google.com",
+ "http://google.com");
+
+ testRegex("naked .org URL", url,
+ "see en.wikipedia.org for details",
+ "http://en.wikipedia.org");
+
+ testRegex("naked .net URL", url,
+ "example.net",
+ "http://example.net");
+
+ testRegex("naked .edu URL (2 instances)", url,
+ "MIT web site is at web.mit.edu and also www.mit.edu",
+ "http://web.mit.edu");
+
+ testRegex("don't match e-mail addresses", url,
+ "test@lindenlab.com",
+ "");
+
+ testRegex(".com URL with path", url,
+ "see secondlife.com/status for grid status",
+ "http://secondlife.com/status");
+
+ testRegex(".com URL with port", url,
+ "secondlife.com:80",
+ "http://secondlife.com:80");
+
+ testRegex(".com URL with port and path", url,
+ "see secondlife.com:80/status",
+ "http://secondlife.com:80/status");
+
+ testRegex("www.*.com URL with port and path", url,
+ "see www.secondlife.com:80/status",
+ "http://www.secondlife.com:80/status");
+
+ testRegex("invalid .com URL [1]", url,
+ "..com",
+ "");
+
+ testRegex("invalid .com URL [2]", url,
+ "you.come",
+ "");
+
+ testRegex("invalid .com URL [3]", url,
+ "recommended",
+ "");
+
+ testRegex("invalid .edu URL", url,
+ "hi there scheduled maitenance has begun",
+ "");
+
+ 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
new file mode 100644
index 0000000000..4e38bea1bd
--- /dev/null
+++ b/indra/llui/tests/llurlmatch_test.cpp
@@ -0,0 +1,187 @@
+/**
+ * @file llurlmatch_test.cpp
+ * @author Martin Reddy
+ * @brief Unit tests for LLUrlMatch
+ *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "../llurlmatch.h"
+#include "lltut.h"
+
+// link seam
+LLUIColor::LLUIColor()
+ : mColorPtr(NULL)
+{}
+
+namespace tut
+{
+ struct LLUrlMatchData
+ {
+ };
+
+ typedef test_group<LLUrlMatchData> factory;
+ typedef factory::object object;
+}
+
+namespace
+{
+ tut::factory tf("LLUrlMatch");
+}
+
+namespace tut
+{
+ template<> template<>
+ void object::test<1>()
+ {
+ //
+ // test the empty() method
+ //
+ LLUrlMatch match;
+ ensure("empty()", match.empty());
+
+ match.setValues(0, 1, "http://secondlife.com", "Second Life", "", "", LLUIColor(), "", "", false,LLUUID::null);
+ ensure("! empty()", ! match.empty());
+ }
+
+ template<> template<>
+ void object::test<2>()
+ {
+ //
+ // test the getStart() method
+ //
+ LLUrlMatch match;
+ ensure_equals("getStart() == 0", match.getStart(), 0);
+
+ match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false,LLUUID::null);
+ ensure_equals("getStart() == 10", match.getStart(), 10);
+ }
+
+ template<> template<>
+ void object::test<3>()
+ {
+ //
+ // test the getEnd() method
+ //
+ LLUrlMatch match;
+ ensure_equals("getEnd() == 0", match.getEnd(), 0);
+
+ match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false,LLUUID::null);
+ ensure_equals("getEnd() == 20", match.getEnd(), 20);
+ }
+
+ template<> template<>
+ void object::test<4>()
+ {
+ //
+ // test the getUrl() method
+ //
+ LLUrlMatch match;
+ ensure_equals("getUrl() == ''", match.getUrl(), "");
+
+ match.setValues(10, 20, "http://slurl.com/", "", "", "", LLUIColor(), "", "", false,LLUUID::null);
+ ensure_equals("getUrl() == 'http://slurl.com/'", match.getUrl(), "http://slurl.com/");
+
+ match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false,LLUUID::null);
+ ensure_equals("getUrl() == '' (2)", match.getUrl(), "");
+ }
+
+ template<> template<>
+ void object::test<5>()
+ {
+ //
+ // test the getLabel() method
+ //
+ LLUrlMatch match;
+ ensure_equals("getLabel() == ''", match.getLabel(), "");
+
+ match.setValues(10, 20, "", "Label", "", "", LLUIColor(), "", "", false,LLUUID::null);
+ ensure_equals("getLabel() == 'Label'", match.getLabel(), "Label");
+
+ match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false,LLUUID::null);
+ ensure_equals("getLabel() == '' (2)", match.getLabel(), "");
+ }
+
+ template<> template<>
+ void object::test<6>()
+ {
+ //
+ // test the getTooltip() method
+ //
+ LLUrlMatch match;
+ ensure_equals("getTooltip() == ''", match.getTooltip(), "");
+
+ match.setValues(10, 20, "", "", "Info", "", LLUIColor(), "", "", false,LLUUID::null);
+ ensure_equals("getTooltip() == 'Info'", match.getTooltip(), "Info");
+
+ match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false,LLUUID::null);
+ ensure_equals("getTooltip() == '' (2)", match.getTooltip(), "");
+ }
+
+ template<> template<>
+ void object::test<7>()
+ {
+ //
+ // test the getIcon() method
+ //
+ LLUrlMatch match;
+ ensure_equals("getIcon() == ''", match.getIcon(), "");
+
+ match.setValues(10, 20, "", "", "", "Icon", LLUIColor(), "", "", false,LLUUID::null);
+ ensure_equals("getIcon() == 'Icon'", match.getIcon(), "Icon");
+
+ match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false,LLUUID::null);
+ ensure_equals("getIcon() == '' (2)", match.getIcon(), "");
+ }
+
+ template<> template<>
+ void object::test<8>()
+ {
+ //
+ // test the getMenuName() method
+ //
+ LLUrlMatch match;
+ ensure("getMenuName() empty", match.getMenuName().empty());
+
+ match.setValues(10, 20, "", "", "", "Icon", LLUIColor(), "xui_file.xml", "", false,LLUUID::null);
+ ensure_equals("getMenuName() == \"xui_file.xml\"", match.getMenuName(), "xui_file.xml");
+
+ match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false,LLUUID::null);
+ ensure("getMenuName() empty (2)", match.getMenuName().empty());
+ }
+
+ template<> template<>
+ void object::test<9>()
+ {
+ //
+ // test the getLocation() method
+ //
+ LLUrlMatch match;
+ ensure("getLocation() empty", match.getLocation().empty());
+
+ match.setValues(10, 20, "", "", "", "Icon", LLUIColor(), "xui_file.xml", "Paris", false,LLUUID::null);
+ ensure_equals("getLocation() == \"Paris\"", match.getLocation(), "Paris");
+
+ match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "", false,LLUUID::null);
+ ensure("getLocation() empty (2)", match.getLocation().empty());
+ }
+}