summaryrefslogtreecommitdiff
path: root/indra/llui
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llui')
-rwxr-xr-x[-rw-r--r--]indra/llui/CMakeLists.txt61
-rwxr-xr-x[-rw-r--r--]indra/llui/llaccordionctrl.cpp14
-rwxr-xr-x[-rw-r--r--]indra/llui/llaccordionctrl.h0
-rwxr-xr-x[-rw-r--r--]indra/llui/llaccordionctrltab.cpp14
-rwxr-xr-x[-rw-r--r--]indra/llui/llaccordionctrltab.h2
-rwxr-xr-xindra/llui/llbadge.cpp381
-rwxr-xr-xindra/llui/llbadge.h174
-rwxr-xr-x[-rw-r--r--]indra/llui/llbadgeholder.cpp (renamed from indra/llui/llfunctorregistry.cpp)34
-rwxr-xr-xindra/llui/llbadgeholder.h56
-rwxr-xr-xindra/llui/llbadgeowner.cpp104
-rwxr-xr-xindra/llui/llbadgeowner.h59
-rwxr-xr-x[-rw-r--r--]indra/llui/llbutton.cpp320
-rwxr-xr-x[-rw-r--r--]indra/llui/llbutton.h68
-rwxr-xr-x[-rw-r--r--]indra/llui/llcallbackmap.h0
-rwxr-xr-xindra/llui/llchat.h109
-rwxr-xr-xindra/llui/llchatentry.cpp251
-rwxr-xr-xindra/llui/llchatentry.h106
-rwxr-xr-x[-rw-r--r--]indra/llui/llcheckboxctrl.cpp16
-rwxr-xr-x[-rw-r--r--]indra/llui/llcheckboxctrl.h2
-rwxr-xr-x[-rw-r--r--]indra/llui/llclipboard.cpp133
-rwxr-xr-x[-rw-r--r--]indra/llui/llclipboard.h68
-rwxr-xr-x[-rw-r--r--]indra/llui/llcombobox.cpp61
-rwxr-xr-x[-rw-r--r--]indra/llui/llcombobox.h12
-rwxr-xr-xindra/llui/llcommandmanager.cpp174
-rwxr-xr-xindra/llui/llcommandmanager.h208
-rwxr-xr-x[-rw-r--r--]indra/llui/llconsole.cpp20
-rwxr-xr-x[-rw-r--r--]indra/llui/llconsole.h3
-rwxr-xr-x[-rw-r--r--]indra/llui/llcontainerview.cpp30
-rwxr-xr-x[-rw-r--r--]indra/llui/llcontainerview.h6
-rwxr-xr-x[-rw-r--r--]indra/llui/llctrlselectioninterface.cpp0
-rwxr-xr-x[-rw-r--r--]indra/llui/llctrlselectioninterface.h2
-rwxr-xr-x[-rw-r--r--]indra/llui/lldockablefloater.cpp24
-rwxr-xr-x[-rw-r--r--]indra/llui/lldockablefloater.h4
-rwxr-xr-x[-rw-r--r--]indra/llui/lldockcontrol.cpp98
-rwxr-xr-x[-rw-r--r--]indra/llui/lldockcontrol.h8
-rwxr-xr-x[-rw-r--r--]indra/llui/lldraghandle.cpp12
-rwxr-xr-x[-rw-r--r--]indra/llui/lldraghandle.h4
-rwxr-xr-x[-rw-r--r--]indra/llui/lleditmenuhandler.cpp0
-rwxr-xr-x[-rw-r--r--]indra/llui/lleditmenuhandler.h0
-rwxr-xr-x[-rw-r--r--]indra/llui/llf32uictrl.cpp0
-rwxr-xr-x[-rw-r--r--]indra/llui/llf32uictrl.h0
-rwxr-xr-x[-rw-r--r--]indra/llui/llfiltereditor.cpp0
-rwxr-xr-x[-rw-r--r--]indra/llui/llfiltereditor.h7
-rwxr-xr-xindra/llui/llflashtimer.cpp98
-rwxr-xr-xindra/llui/llflashtimer.h74
-rwxr-xr-x[-rw-r--r--]indra/llui/llflatlistview.cpp7
-rwxr-xr-x[-rw-r--r--]indra/llui/llflatlistview.h3
-rwxr-xr-x[-rw-r--r--]indra/llui/llfloater.cpp1139
-rwxr-xr-x[-rw-r--r--]indra/llui/llfloater.h179
-rwxr-xr-x[-rw-r--r--]indra/llui/llfloaterreg.cpp292
-rwxr-xr-x[-rw-r--r--]indra/llui/llfloaterreg.h22
-rwxr-xr-x[-rw-r--r--]indra/llui/llfloaterreglistener.cpp17
-rwxr-xr-x[-rw-r--r--]indra/llui/llfloaterreglistener.h1
-rwxr-xr-x[-rw-r--r--]indra/llui/llflyoutbutton.cpp0
-rwxr-xr-x[-rw-r--r--]indra/llui/llflyoutbutton.h2
-rwxr-xr-x[-rw-r--r--]indra/llui/llfocusmgr.cpp41
-rwxr-xr-x[-rw-r--r--]indra/llui/llfocusmgr.h15
-rwxr-xr-xindra/llui/llfolderview.cpp1975
-rwxr-xr-xindra/llui/llfolderview.h407
-rw-r--r--indra/llui/llfolderviewitem.cpp2126
-rwxr-xr-xindra/llui/llfolderviewitem.h465
-rwxr-xr-xindra/llui/llfolderviewmodel.cpp68
-rwxr-xr-xindra/llui/llfolderviewmodel.h481
-rwxr-xr-x[-rw-r--r--]indra/llui/llfunctorregistry.h10
-rw-r--r--indra/llui/llhandle.h164
-rwxr-xr-x[-rw-r--r--]indra/llui/llhelp.h1
-rwxr-xr-x[-rw-r--r--]indra/llui/lliconctrl.cpp31
-rwxr-xr-x[-rw-r--r--]indra/llui/lliconctrl.h13
-rwxr-xr-x[-rw-r--r--]indra/llui/llkeywords.cpp513
-rwxr-xr-x[-rw-r--r--]indra/llui/llkeywords.h122
-rwxr-xr-x[-rw-r--r--]indra/llui/lllayoutstack.cpp1099
-rwxr-xr-x[-rw-r--r--]indra/llui/lllayoutstack.h167
-rwxr-xr-x[-rw-r--r--]indra/llui/lllazyvalue.h0
-rwxr-xr-x[-rw-r--r--]indra/llui/lllineeditor.cpp533
-rwxr-xr-x[-rw-r--r--]indra/llui/lllineeditor.h50
-rwxr-xr-x[-rw-r--r--]indra/llui/llloadingindicator.cpp66
-rwxr-xr-x[-rw-r--r--]indra/llui/llloadingindicator.h33
-rwxr-xr-x[-rw-r--r--]indra/llui/lllocalcliprect.cpp8
-rwxr-xr-x[-rw-r--r--]indra/llui/lllocalcliprect.h0
-rwxr-xr-x[-rw-r--r--]indra/llui/llmenubutton.cpp86
-rwxr-xr-x[-rw-r--r--]indra/llui/llmenubutton.h30
-rwxr-xr-x[-rw-r--r--]indra/llui/llmenugl.cpp535
-rwxr-xr-x[-rw-r--r--]indra/llui/llmenugl.h105
-rwxr-xr-x[-rw-r--r--]indra/llui/llmodaldialog.cpp43
-rwxr-xr-x[-rw-r--r--]indra/llui/llmodaldialog.h2
-rwxr-xr-x[-rw-r--r--]indra/llui/llmultifloater.cpp62
-rwxr-xr-x[-rw-r--r--]indra/llui/llmultifloater.h21
-rwxr-xr-x[-rw-r--r--]indra/llui/llmultislider.cpp15
-rwxr-xr-x[-rw-r--r--]indra/llui/llmultislider.h0
-rwxr-xr-x[-rw-r--r--]indra/llui/llmultisliderctrl.cpp2
-rwxr-xr-x[-rw-r--r--]indra/llui/llmultisliderctrl.h0
-rwxr-xr-x[-rw-r--r--]indra/llui/llnotificationptr.h0
-rwxr-xr-x[-rw-r--r--]indra/llui/llnotifications.cpp692
-rwxr-xr-x[-rw-r--r--]indra/llui/llnotifications.h329
-rw-r--r--indra/llui/llnotificationslistener.cpp16
-rwxr-xr-x[-rw-r--r--]indra/llui/llnotificationsutil.cpp0
-rwxr-xr-x[-rw-r--r--]indra/llui/llnotificationsutil.h1
-rwxr-xr-x[-rw-r--r--]indra/llui/llnotificationtemplate.h85
-rwxr-xr-x[-rw-r--r--]indra/llui/llnotificationvisibilityrule.h2
-rwxr-xr-x[-rw-r--r--]indra/llui/llpanel.cpp278
-rwxr-xr-x[-rw-r--r--]indra/llui/llpanel.h70
-rwxr-xr-x[-rw-r--r--]indra/llui/llprogressbar.cpp3
-rwxr-xr-x[-rw-r--r--]indra/llui/llprogressbar.h3
-rwxr-xr-x[-rw-r--r--]indra/llui/llradiogroup.cpp14
-rwxr-xr-x[-rw-r--r--]indra/llui/llradiogroup.h0
-rwxr-xr-x[-rw-r--r--]indra/llui/llresizebar.cpp101
-rwxr-xr-x[-rw-r--r--]indra/llui/llresizebar.h42
-rwxr-xr-x[-rw-r--r--]indra/llui/llresizehandle.cpp58
-rwxr-xr-x[-rw-r--r--]indra/llui/llresizehandle.h2
-rwxr-xr-x[-rw-r--r--]indra/llui/llresmgr.cpp31
-rwxr-xr-x[-rw-r--r--]indra/llui/llresmgr.h0
-rwxr-xr-x[-rw-r--r--]indra/llui/llrngwriter.cpp10
-rwxr-xr-x[-rw-r--r--]indra/llui/llrngwriter.h0
-rwxr-xr-x[-rw-r--r--]indra/llui/llscrollbar.cpp17
-rwxr-xr-x[-rw-r--r--]indra/llui/llscrollbar.h9
-rwxr-xr-x[-rw-r--r--]indra/llui/llscrollcontainer.cpp173
-rwxr-xr-x[-rw-r--r--]indra/llui/llscrollcontainer.h15
-rwxr-xr-x[-rw-r--r--]indra/llui/llscrollingpanellist.cpp2
-rwxr-xr-x[-rw-r--r--]indra/llui/llscrollingpanellist.h7
-rwxr-xr-x[-rw-r--r--]indra/llui/llscrolllistcell.cpp6
-rwxr-xr-x[-rw-r--r--]indra/llui/llscrolllistcell.h0
-rwxr-xr-x[-rw-r--r--]indra/llui/llscrolllistcolumn.cpp11
-rwxr-xr-x[-rw-r--r--]indra/llui/llscrolllistcolumn.h6
-rwxr-xr-x[-rw-r--r--]indra/llui/llscrolllistctrl.cpp284
-rwxr-xr-x[-rw-r--r--]indra/llui/llscrolllistctrl.h40
-rwxr-xr-x[-rw-r--r--]indra/llui/llscrolllistitem.cpp5
-rwxr-xr-x[-rw-r--r--]indra/llui/llscrolllistitem.h2
-rw-r--r--indra/llui/llsdparam.cpp314
-rw-r--r--indra/llui/llsdparam.h104
-rwxr-xr-x[-rw-r--r--]indra/llui/llsearcheditor.cpp0
-rwxr-xr-x[-rw-r--r--]indra/llui/llsearcheditor.h4
-rwxr-xr-x[-rw-r--r--]indra/llui/llslider.cpp8
-rwxr-xr-x[-rw-r--r--]indra/llui/llslider.h5
-rwxr-xr-x[-rw-r--r--]indra/llui/llsliderctrl.cpp84
-rwxr-xr-x[-rw-r--r--]indra/llui/llsliderctrl.h5
-rwxr-xr-xindra/llui/llspellcheck.cpp512
-rwxr-xr-xindra/llui/llspellcheck.h93
-rwxr-xr-xindra/llui/llspellcheckmenuhandler.h46
-rwxr-xr-x[-rw-r--r--]indra/llui/llspinctrl.cpp36
-rwxr-xr-x[-rw-r--r--]indra/llui/llspinctrl.h5
-rwxr-xr-x[-rw-r--r--]indra/llui/llstatbar.cpp751
-rwxr-xr-x[-rw-r--r--]indra/llui/llstatbar.h121
-rwxr-xr-x[-rw-r--r--]indra/llui/llstatgraph.cpp91
-rwxr-xr-x[-rw-r--r--]indra/llui/llstatgraph.h99
-rwxr-xr-x[-rw-r--r--]indra/llui/llstatview.cpp0
-rwxr-xr-x[-rw-r--r--]indra/llui/llstatview.h3
-rwxr-xr-x[-rw-r--r--]indra/llui/llstyle.cpp2
-rwxr-xr-x[-rw-r--r--]indra/llui/llstyle.h5
-rwxr-xr-x[-rw-r--r--]indra/llui/lltabcontainer.cpp333
-rwxr-xr-x[-rw-r--r--]indra/llui/lltabcontainer.h11
-rwxr-xr-x[-rw-r--r--]indra/llui/lltextbase.cpp1040
-rwxr-xr-x[-rw-r--r--]indra/llui/lltextbase.h175
-rwxr-xr-x[-rw-r--r--]indra/llui/lltextbox.cpp15
-rwxr-xr-x[-rw-r--r--]indra/llui/lltextbox.h0
-rwxr-xr-x[-rw-r--r--]indra/llui/lltexteditor.cpp499
-rwxr-xr-x[-rw-r--r--]indra/llui/lltexteditor.h86
-rwxr-xr-x[-rw-r--r--]indra/llui/lltextparser.cpp4
-rwxr-xr-x[-rw-r--r--]indra/llui/lltextparser.h0
-rwxr-xr-x[-rw-r--r--]indra/llui/lltextutil.cpp4
-rwxr-xr-x[-rw-r--r--]indra/llui/lltextutil.h10
-rwxr-xr-x[-rw-r--r--]indra/llui/lltextvalidate.cpp35
-rwxr-xr-x[-rw-r--r--]indra/llui/lltextvalidate.h1
-rwxr-xr-xindra/llui/lltimectrl.cpp433
-rwxr-xr-xindra/llui/lltimectrl.h131
-rwxr-xr-x[-rw-r--r--]indra/llui/lltoggleablemenu.cpp11
-rwxr-xr-x[-rw-r--r--]indra/llui/lltoggleablemenu.h6
-rwxr-xr-xindra/llui/lltoolbar.cpp1259
-rwxr-xr-xindra/llui/lltoolbar.h331
-rwxr-xr-x[-rw-r--r--]indra/llui/lltooltip.cpp46
-rwxr-xr-x[-rw-r--r--]indra/llui/lltooltip.h4
-rwxr-xr-xindra/llui/lltrans.cpp295
-rwxr-xr-xindra/llui/lltrans.h134
-rwxr-xr-x[-rw-r--r--]indra/llui/lltransutil.cpp18
-rwxr-xr-x[-rw-r--r--]indra/llui/lltransutil.h0
-rwxr-xr-x[-rw-r--r--]indra/llui/llui.cpp1851
-rwxr-xr-x[-rw-r--r--]indra/llui/llui.h419
-rwxr-xr-xindra/llui/lluicolor.cpp87
-rwxr-xr-xindra/llui/lluicolor.h71
-rwxr-xr-x[-rw-r--r--]indra/llui/lluicolortable.cpp28
-rwxr-xr-x[-rw-r--r--]indra/llui/lluicolortable.h2
-rwxr-xr-x[-rw-r--r--]indra/llui/lluiconstants.h0
-rwxr-xr-x[-rw-r--r--]indra/llui/lluictrl.cpp263
-rwxr-xr-x[-rw-r--r--]indra/llui/lluictrl.h25
-rwxr-xr-x[-rw-r--r--]indra/llui/lluictrlfactory.cpp89
-rwxr-xr-x[-rw-r--r--]indra/llui/lluictrlfactory.h89
-rwxr-xr-x[-rw-r--r--]indra/llui/lluifwd.h0
-rw-r--r--indra/llui/lluiimage.cpp199
-rw-r--r--indra/llui/lluiimage.h123
-rwxr-xr-x[-rw-r--r--]indra/llui/lluistring.cpp20
-rwxr-xr-x[-rw-r--r--]indra/llui/lluistring.h6
-rwxr-xr-x[-rw-r--r--]indra/llui/llundo.cpp2
-rwxr-xr-x[-rw-r--r--]indra/llui/llundo.h0
-rwxr-xr-x[-rw-r--r--]indra/llui/llurlaction.cpp105
-rwxr-xr-x[-rw-r--r--]indra/llui/llurlaction.h30
-rwxr-xr-x[-rw-r--r--]indra/llui/llurlentry.cpp253
-rwxr-xr-x[-rw-r--r--]indra/llui/llurlentry.h67
-rwxr-xr-x[-rw-r--r--]indra/llui/llurlmatch.cpp11
-rwxr-xr-x[-rw-r--r--]indra/llui/llurlmatch.h12
-rwxr-xr-x[-rw-r--r--]indra/llui/llurlregistry.cpp62
-rwxr-xr-x[-rw-r--r--]indra/llui/llurlregistry.h8
-rwxr-xr-x[-rw-r--r--]indra/llui/llview.cpp1319
-rwxr-xr-x[-rw-r--r--]indra/llui/llview.h164
-rwxr-xr-x[-rw-r--r--]indra/llui/llviewborder.cpp4
-rwxr-xr-x[-rw-r--r--]indra/llui/llviewborder.h0
-rw-r--r--indra/llui/llviewereventrecorder.cpp296
-rw-r--r--indra/llui/llviewereventrecorder.h103
-rwxr-xr-xindra/llui/llviewinject.cpp49
-rwxr-xr-xindra/llui/llviewinject.h56
-rwxr-xr-x[-rw-r--r--]indra/llui/llviewmodel.cpp18
-rwxr-xr-x[-rw-r--r--]indra/llui/llviewmodel.h8
-rwxr-xr-x[-rw-r--r--]indra/llui/llviewquery.cpp25
-rwxr-xr-x[-rw-r--r--]indra/llui/llviewquery.h7
-rwxr-xr-x[-rw-r--r--]indra/llui/llwindowshade.cpp212
-rwxr-xr-x[-rw-r--r--]indra/llui/llwindowshade.h17
-rwxr-xr-xindra/llui/llxuiparser.cpp1785
-rwxr-xr-xindra/llui/llxuiparser.h254
-rwxr-xr-x[-rw-r--r--]indra/llui/tests/llurlentry_stub.cpp61
-rwxr-xr-x[-rw-r--r--]indra/llui/tests/llurlentry_test.cpp2
-rwxr-xr-x[-rw-r--r--]indra/llui/tests/llurlmatch_test.cpp83
219 files changed, 23354 insertions, 8182 deletions
diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt
index 33ab2e93b5..52738aeb6f 100644..100755
--- a/indra/llui/CMakeLists.txt
+++ b/indra/llui/CMakeLists.txt
@@ -5,33 +5,43 @@ project(llui)
include(00-Common)
include(LLCommon)
include(LLImage)
+include(LLInventory)
include(LLMath)
include(LLMessage)
include(LLRender)
include(LLWindow)
include(LLVFS)
include(LLXML)
-include(LLXUIXML)
include_directories(
${LLCOMMON_INCLUDE_DIRS}
${LLIMAGE_INCLUDE_DIRS}
+ ${LLINVENTORY_INCLUDE_DIRS}
${LLMATH_INCLUDE_DIRS}
${LLMESSAGE_INCLUDE_DIRS}
${LLRENDER_INCLUDE_DIRS}
${LLWINDOW_INCLUDE_DIRS}
${LLVFS_INCLUDE_DIRS}
${LLXML_INCLUDE_DIRS}
- ${LLXUIXML_INCLUDE_DIRS}
+ ${LIBS_PREBUILD_DIR}/include/hunspell
+ )
+include_directories(SYSTEM
+ ${LLCOMMON_SYSTEM_INCLUDE_DIRS}
+ ${LLXML_SYSTEM_INCLUDE_DIRS}
)
set(llui_SOURCE_FILES
llaccordionctrl.cpp
llaccordionctrltab.cpp
+ llbadge.cpp
+ llbadgeholder.cpp
+ llbadgeowner.cpp
llbutton.cpp
+ llchatentry.cpp
llcheckboxctrl.cpp
llclipboard.cpp
llcombobox.cpp
+ llcommandmanager.cpp
llconsole.cpp
llcontainerview.cpp
llctrlselectioninterface.cpp
@@ -41,13 +51,16 @@ set(llui_SOURCE_FILES
lleditmenuhandler.cpp
llf32uictrl.cpp
llfiltereditor.cpp
+ llflashtimer.cpp
llflatlistview.cpp
llfloater.cpp
llfloaterreg.cpp
llfloaterreglistener.cpp
llflyoutbutton.cpp
llfocusmgr.cpp
- llfunctorregistry.cpp
+ llfolderview.cpp
+ llfolderviewitem.cpp
+ llfolderviewmodel.cpp
lliconctrl.cpp
llkeywords.cpp
lllayoutstack.cpp
@@ -77,10 +90,10 @@ set(llui_SOURCE_FILES
llscrolllistcolumn.cpp
llscrolllistctrl.cpp
llscrolllistitem.cpp
- llsdparam.cpp
llsearcheditor.cpp
llslider.cpp
llsliderctrl.cpp
+ llspellcheck.cpp
llspinctrl.cpp
llstatbar.cpp
llstatgraph.cpp
@@ -93,14 +106,17 @@ set(llui_SOURCE_FILES
lltextparser.cpp
lltextutil.cpp
lltextvalidate.cpp
+ lltimectrl.cpp
+ lltrans.cpp
lltransutil.cpp
lltoggleablemenu.cpp
+ lltoolbar.cpp
lltooltip.cpp
llui.cpp
+ lluicolor.cpp
lluicolortable.cpp
lluictrl.cpp
lluictrlfactory.cpp
- lluiimage.cpp
lluistring.cpp
llundo.cpp
llurlaction.cpp
@@ -108,10 +124,13 @@ set(llui_SOURCE_FILES
llurlmatch.cpp
llurlregistry.cpp
llviewborder.cpp
+ llviewinject.cpp
llviewmodel.cpp
llview.cpp
llviewquery.cpp
+ llviewereventrecorder.cpp
llwindowshade.cpp
+ llxuiparser.cpp
)
set(llui_HEADER_FILES
@@ -119,11 +138,17 @@ set(llui_HEADER_FILES
llaccordionctrl.h
llaccordionctrltab.h
+ llbadge.h
+ llbadgeholder.h
+ llbadgeowner.h
llbutton.h
llcallbackmap.h
+ llchatentry.h
+ llchat.h
llcheckboxctrl.h
llclipboard.h
llcombobox.h
+ llcommandmanager.h
llconsole.h
llcontainerview.h
llctrlselectioninterface.h
@@ -133,14 +158,17 @@ set(llui_HEADER_FILES
lleditmenuhandler.h
llf32uictrl.h
llfiltereditor.h
+ llflashtimer.h
llflatlistview.h
llfloater.h
llfloaterreg.h
llfloaterreglistener.h
llflyoutbutton.h
llfocusmgr.h
+ llfolderview.h
+ llfolderviewitem.h
+ llfolderviewmodel.h
llfunctorregistry.h
- llhandle.h
llhelp.h
lliconctrl.h
llkeywords.h
@@ -160,7 +188,7 @@ set(llui_HEADER_FILES
llnotificationslistener.h
llnotificationsutil.h
llnotificationtemplate.h
- llnotificationvisibilityrule.h
+ llnotificationvisibilityrule.h
llpanel.h
llprogressbar.h
llradiogroup.h
@@ -176,9 +204,10 @@ set(llui_HEADER_FILES
llscrolllistcolumn.h
llscrolllistctrl.h
llscrolllistitem.h
- llsdparam.h
llsliderctrl.h
llslider.h
+ llspellcheck.h
+ llspellcheckmenuhandler.h
llspinctrl.h
llstatbar.h
llstatgraph.h
@@ -191,8 +220,11 @@ set(llui_HEADER_FILES
lltextparser.h
lltextutil.h
lltextvalidate.h
+ lltimectrl.h
lltoggleablemenu.h
+ lltoolbar.h
lltooltip.h
+ lltrans.h
lltransutil.h
lluicolortable.h
lluiconstants.h
@@ -200,7 +232,7 @@ set(llui_HEADER_FILES
lluictrl.h
lluifwd.h
llui.h
- lluiimage.h
+ lluicolor.h
lluistring.h
llundo.h
llurlaction.h
@@ -208,10 +240,13 @@ set(llui_HEADER_FILES
llurlmatch.h
llurlregistry.h
llviewborder.h
+ llviewinject.h
llviewmodel.h
llview.h
+ llviewereventrecorder.h
llviewquery.h
llwindowshade.h
+ llxuiparser.h
)
set_source_files_properties(${llui_HEADER_FILES}
@@ -237,19 +272,23 @@ target_link_libraries(llui
${LLRENDER_LIBRARIES}
${LLWINDOW_LIBRARIES}
${LLIMAGE_LIBRARIES}
+ ${LLINVENTORY_LIBRARIES}
${LLVFS_LIBRARIES} # ugh, just for LLDir
${LLXUIXML_LIBRARIES}
${LLXML_LIBRARIES}
${LLMATH_LIBRARIES}
+ ${HUNSPELL_LIBRARY}
${LLCOMMON_LIBRARIES} # must be after llimage, llwindow, llrender
)
+# Add tests
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}")
+ # INTEGRATION TESTS
+ set(test_libs llui llmessage llcommon ${LLCOMMON_LIBRARIES} ${WINDOWS_LIBRARIES})
+ LL_ADD_INTEGRATION_TEST(llurlentry llurlentry.cpp "${test_libs}")
endif(LL_TESTS)
diff --git a/indra/llui/llaccordionctrl.cpp b/indra/llui/llaccordionctrl.cpp
index d636161baf..623f570cef 100644..100755
--- a/indra/llui/llaccordionctrl.cpp
+++ b/indra/llui/llaccordionctrl.cpp
@@ -36,25 +36,17 @@
#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 )
@@ -69,7 +61,7 @@ LLAccordionCtrl::LLAccordionCtrl(const Params& params):LLPanel(params)
mSingleExpansion = params.single_expansion;
if(mFitParent && !mSingleExpansion)
{
- llinfos << "fit_parent works best when combined with single_expansion" << llendl;
+ LL_INFOS() << "fit_parent works best when combined with single_expansion" << LL_ENDL;
}
}
@@ -613,7 +605,7 @@ BOOL LLAccordionCtrl::autoScroll (S32 x, S32 y)
// 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());
+ S32 auto_scroll_speed = ll_round(mAutoScrollRate * LLFrameTimer::getFrameDeltaTimeF32());
LLRect bottom_scroll_rect = screen_local_extents;
bottom_scroll_rect.mTop = rect_local.mBottom + auto_scroll_region_height;
@@ -845,7 +837,7 @@ void LLAccordionCtrl::sort()
{
if (!mTabComparator)
{
- llwarns << "No comparator specified for sorting accordion tabs." << llendl;
+ LL_WARNS() << "No comparator specified for sorting accordion tabs." << LL_ENDL;
return;
}
diff --git a/indra/llui/llaccordionctrl.h b/indra/llui/llaccordionctrl.h
index 1fe64c472e..1fe64c472e 100644..100755
--- a/indra/llui/llaccordionctrl.h
+++ b/indra/llui/llaccordionctrl.h
diff --git a/indra/llui/llaccordionctrltab.cpp b/indra/llui/llaccordionctrltab.cpp
index 9e4849c58b..53720a6044 100644..100755
--- a/indra/llui/llaccordionctrltab.cpp
+++ b/indra/llui/llaccordionctrltab.cpp
@@ -184,7 +184,7 @@ void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::setTitleFontStyle(std::string
if (mHeaderTextbox)
{
std::string text = mHeaderTextbox->getText();
- mStyleParams.font(mHeaderTextbox->getDefaultFont());
+ mStyleParams.font(mHeaderTextbox->getFont());
mStyleParams.font.style(style);
mHeaderTextbox->setText(text, mStyleParams);
}
@@ -339,7 +339,7 @@ LLAccordionCtrlTab::Params::Params()
,fit_panel("fit_panel",true)
,selection_enabled("selection_enabled", false)
{
- mouse_opaque(false);
+ changeDefault(mouse_opaque, false);
}
LLAccordionCtrlTab::LLAccordionCtrlTab(const LLAccordionCtrlTab::Params&p)
@@ -444,9 +444,9 @@ void LLAccordionCtrlTab::changeOpenClose(bool is_open)
}
}
-void LLAccordionCtrlTab::handleVisibilityChange(BOOL new_visibility)
+void LLAccordionCtrlTab::onVisibilityChange(BOOL new_visibility)
{
- LLUICtrl::handleVisibilityChange(new_visibility);
+ LLUICtrl::onVisibilityChange(new_visibility);
notifyParent(LLSD().with("child_visibility_change", new_visibility));
}
@@ -973,10 +973,10 @@ void LLAccordionCtrlTab::drawChild(const LLRect& root_rect,LLView* child)
if ( root_rect.overlaps(screen_rect) && LLUI::sDirtyRect.overlaps(screen_rect))
{
- glMatrixMode(GL_MODELVIEW);
+ gGL.matrixMode(LLRender::MM_MODELVIEW);
LLUI::pushMatrix();
{
- LLUI::translate((F32)child->getRect().mLeft, (F32)child->getRect().mBottom, 0.f);
+ LLUI::translate((F32)child->getRect().mLeft, (F32)child->getRect().mBottom);
child->draw();
}
@@ -1022,7 +1022,7 @@ void LLAccordionCtrlTab::updateLayout ( const LLRect& child_rect )
S32 panel_width = child_rect.getWidth();
static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
- if(mScrollbar->getVisible() != false)
+ if(mScrollbar && mScrollbar->getVisible() != false)
{
panel_top+=mScrollbar->getDocPos();
panel_width-=scrollbar_size;
diff --git a/indra/llui/llaccordionctrltab.h b/indra/llui/llaccordionctrltab.h
index dddaa581e6..7a78700e0f 100644..100755
--- a/indra/llui/llaccordionctrltab.h
+++ b/indra/llui/llaccordionctrltab.h
@@ -158,7 +158,7 @@ public:
/**
* Raises notifyParent event with "child_visibility_change" = new_visibility
*/
- void handleVisibilityChange(BOOL new_visibility);
+ void onVisibilityChange(BOOL new_visibility);
// Changes expand/collapse state and triggers expand/collapse callbacks
virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
diff --git a/indra/llui/llbadge.cpp b/indra/llui/llbadge.cpp
new file mode 100755
index 0000000000..42726de0ad
--- /dev/null
+++ b/indra/llui/llbadge.cpp
@@ -0,0 +1,381 @@
+/**
+ * @file llbadge.cpp
+ * @brief Implementation for badges
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#define LLBADGE_CPP
+#include "llbadge.h"
+
+#include "llscrollcontainer.h"
+#include "lluictrlfactory.h"
+
+
+static LLDefaultChildRegistry::Register<LLBadge> r("badge");
+
+static const S32 BADGE_OFFSET_NOT_SPECIFIED = 0x7FFFFFFF;
+
+// Compiler optimization, generate extern template
+template class LLBadge* LLView::getChild<class LLBadge>(const std::string& name, BOOL recurse) const;
+
+
+LLBadge::Params::Params()
+ : image("image")
+ , border_image("border_image")
+ , border_color("border_color")
+ , image_color("image_color")
+ , label("label")
+ , label_color("label_color")
+ , label_offset_horiz("label_offset_horiz")
+ , label_offset_vert("label_offset_vert")
+ , location("location", LLRelPos::TOP_LEFT)
+ , location_offset_hcenter("location_offset_hcenter")
+ , location_offset_vcenter("location_offset_vcenter")
+ , location_percent_hcenter("location_percent_hcenter")
+ , location_percent_vcenter("location_percent_vcenter")
+ , padding_horiz("padding_horiz")
+ , padding_vert("padding_vert")
+{}
+
+bool LLBadge::Params::equals(const Params& a) const
+{
+ bool comp = true;
+
+ // skip owner in comparison on purpose
+
+ comp &= (border_image() == a.border_image());
+ comp &= (border_color() == a.border_color());
+ comp &= (image() == a.image());
+ comp &= (image_color() == a.image_color());
+ comp &= (label() == a.label());
+ comp &= (label_color() == a.label_color());
+ comp &= (label_offset_horiz() == a.label_offset_horiz());
+ comp &= (label_offset_vert() == a.label_offset_vert());
+ comp &= (location() == a.location());
+ comp &= (location_offset_hcenter() == a.location_offset_hcenter());
+ comp &= (location_offset_vcenter() == a.location_offset_vcenter());
+ comp &= (location_percent_hcenter() == a.location_percent_hcenter());
+ comp &= (location_percent_vcenter() == a.location_percent_vcenter());
+ comp &= (padding_horiz() == a.padding_horiz());
+ comp &= (padding_vert() == a.padding_vert());
+
+ return comp;
+}
+
+LLBadge::LLBadge(const LLBadge::Params& p)
+ : LLUICtrl(p)
+ , mOwner(p.owner)
+ , mBorderImage(p.border_image)
+ , mBorderColor(p.border_color)
+ , mGLFont(p.font)
+ , mImage(p.image)
+ , mImageColor(p.image_color)
+ , mLabel(p.label)
+ , mLabelColor(p.label_color)
+ , mLabelOffsetHoriz(p.label_offset_horiz)
+ , mLabelOffsetVert(p.label_offset_vert)
+ , mLocation(p.location)
+ , mLocationOffsetHCenter(BADGE_OFFSET_NOT_SPECIFIED)
+ , mLocationOffsetVCenter(BADGE_OFFSET_NOT_SPECIFIED)
+ , mLocationPercentHCenter(0.5f)
+ , mLocationPercentVCenter(0.5f)
+ , mPaddingHoriz(p.padding_horiz)
+ , mPaddingVert(p.padding_vert)
+ , mParentScroller(NULL)
+{
+ if (mImage.isNull())
+ {
+ LL_WARNS() << "Badge: " << getName() << " with no image!" << LL_ENDL;
+ }
+
+ if (p.location_offset_hcenter.isProvided())
+ {
+ mLocationOffsetHCenter = p.location_offset_hcenter();
+ }
+
+ if (p.location_offset_vcenter.isProvided())
+ {
+ mLocationOffsetVCenter = p.location_offset_vcenter();
+ }
+
+ //
+ // The following logic is to set the mLocationPercentHCenter and mLocationPercentVCenter
+ // based on the Location enum and our horizontal and vertical location percentages. The
+ // draw code then uses this on the owner rectangle to compute the screen location for
+ // the badge.
+ //
+
+ if (!LLRelPos::IsCenter(mLocation))
+ {
+ F32 h_center = p.location_percent_hcenter * 0.01f;
+ F32 v_center = p.location_percent_vcenter * 0.01f;
+
+ if (LLRelPos::IsRight(mLocation))
+ {
+ mLocationPercentHCenter = 0.5f * (1.0f + h_center);
+ }
+ else if (LLRelPos::IsLeft(mLocation))
+ {
+ mLocationPercentHCenter = 0.5f * (1.0f - h_center);
+ }
+
+ if (LLRelPos::IsTop(mLocation))
+ {
+ mLocationPercentVCenter = 0.5f * (1.0f + v_center);
+ }
+ else if (LLRelPos::IsBottom(mLocation))
+ {
+ mLocationPercentVCenter = 0.5f * (1.0f - v_center);
+ }
+ }
+}
+
+LLBadge::~LLBadge()
+{
+}
+
+bool LLBadge::addToView(LLView * view)
+{
+ bool child_added = view->addChild(this);
+
+ if (child_added)
+ {
+ setShape(view->getLocalRect());
+
+ // Find a parent scroll container, if there is one in case we need it for positioning
+
+ LLView * parent = mOwner.get();
+
+ while ((parent != NULL) && ((mParentScroller = dynamic_cast<LLScrollContainer *>(parent)) == NULL))
+ {
+ parent = parent->getParent();
+ }
+ }
+
+ return child_added;
+}
+
+void LLBadge::setLabel(const LLStringExplicit& label)
+{
+ mLabel = label;
+}
+
+//
+// This is a fallback function to render a rectangle for badges without a valid image
+//
+void renderBadgeBackground(F32 centerX, F32 centerY, F32 width, F32 height, const LLColor4U &color)
+{
+ gGL.pushUIMatrix();
+ gGL.loadUIIdentity();
+ gGL.setSceneBlendType(LLRender::BT_REPLACE);
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+
+ gGL.color4ubv(color.mV);
+ gGL.texCoord2i(0, 0);
+
+ F32 x = LLFontGL::sCurOrigin.mX + centerX - width * 0.5f;
+ F32 y = LLFontGL::sCurOrigin.mY + centerY - height * 0.5f;
+
+ LLRectf screen_rect(ll_round(x),
+ ll_round(y),
+ ll_round(x) + width,
+ ll_round(y) + height);
+
+ LLVector3 vertices[4];
+ vertices[0] = LLVector3(screen_rect.mRight, screen_rect.mTop, 1.0f);
+ vertices[1] = LLVector3(screen_rect.mLeft, screen_rect.mTop, 1.0f);
+ vertices[2] = LLVector3(screen_rect.mLeft, screen_rect.mBottom, 1.0f);
+ vertices[3] = LLVector3(screen_rect.mRight, screen_rect.mBottom, 1.0f);
+
+ gGL.begin(LLRender::QUADS);
+ {
+ gGL.vertexBatchPreTransformed(vertices, 4);
+ }
+ gGL.end();
+
+ gGL.popUIMatrix();
+}
+
+
+// virtual
+void LLBadge::draw()
+{
+ if (!mLabel.empty())
+ {
+ LLView* owner_view = mOwner.get();
+
+ if (owner_view)
+ {
+ //
+ // Calculate badge size based on label text
+ //
+
+ LLWString badge_label_wstring = mLabel;
+
+ S32 badge_label_begin_offset = 0;
+ S32 badge_char_length = S32_MAX;
+ S32 badge_pixel_length = S32_MAX;
+ F32 *right_position_out = NULL;
+ BOOL do_not_use_ellipses = false;
+
+ F32 badge_width = (2.0f * mPaddingHoriz) +
+ mGLFont->getWidthF32(badge_label_wstring.c_str(), badge_label_begin_offset, badge_char_length);
+
+ F32 badge_height = (2.0f * mPaddingVert) + mGLFont->getLineHeight();
+
+ //
+ // Calculate badge position based on owner
+ //
+
+ LLRect owner_rect;
+ owner_view->localRectToOtherView(owner_view->getLocalRect(), & owner_rect, this);
+
+ S32 location_offset_horiz = mLocationOffsetHCenter;
+ S32 location_offset_vert = mLocationOffsetVCenter;
+
+ // If we're in a scroll container, do some math to keep us in the same place on screen if applicable
+ if (mParentScroller != NULL)
+ {
+ LLRect visibleRect = mParentScroller->getVisibleContentRect();
+
+ if (mLocationOffsetHCenter != BADGE_OFFSET_NOT_SPECIFIED)
+ {
+ if (LLRelPos::IsRight(mLocation))
+ {
+ location_offset_horiz += visibleRect.mRight;
+ }
+ else if (LLRelPos::IsLeft(mLocation))
+ {
+ location_offset_horiz += visibleRect.mLeft;
+ }
+ else // center
+ {
+ location_offset_horiz += (visibleRect.mLeft + visibleRect.mRight) / 2;
+ }
+ }
+
+ if (mLocationOffsetVCenter != BADGE_OFFSET_NOT_SPECIFIED)
+ {
+ if (LLRelPos::IsTop(mLocation))
+ {
+ location_offset_vert += visibleRect.mTop;
+ }
+ else if (LLRelPos::IsBottom(mLocation))
+ {
+ location_offset_vert += visibleRect.mBottom;
+ }
+ else // center
+ {
+ location_offset_vert += (visibleRect.mBottom + visibleRect.mTop) / 2;
+ }
+ }
+ }
+
+ F32 badge_center_x;
+ F32 badge_center_y;
+
+ // Compute x position
+ if (mLocationOffsetHCenter == BADGE_OFFSET_NOT_SPECIFIED)
+ {
+ badge_center_x = owner_rect.mLeft + owner_rect.getWidth() * mLocationPercentHCenter;
+ }
+ else
+ {
+ badge_center_x = location_offset_horiz;
+ }
+
+ // Compute y position
+ if (mLocationOffsetVCenter == BADGE_OFFSET_NOT_SPECIFIED)
+ {
+ badge_center_y = owner_rect.mBottom + owner_rect.getHeight() * mLocationPercentVCenter;
+ }
+ else
+ {
+ badge_center_y = location_offset_vert;
+ }
+
+ //
+ // Draw button image, if available.
+ // Otherwise draw basic rectangular button.
+ //
+
+ F32 alpha = getDrawContext().mAlpha;
+
+ if (!mImage.isNull())
+ {
+ F32 badge_x = badge_center_x - badge_width * 0.5f;
+ F32 badge_y = badge_center_y - badge_height * 0.5f;
+
+ mImage->drawSolid((S32) badge_x, (S32) badge_y, (S32) badge_width, (S32) badge_height, mImageColor % alpha);
+
+ if (!mBorderImage.isNull())
+ {
+ mBorderImage->drawSolid((S32) badge_x, (S32) badge_y, (S32) badge_width, (S32) badge_height, mBorderColor % alpha);
+ }
+ }
+ else
+ {
+ LL_DEBUGS() << "No image for badge " << getName() << " on owner " << owner_view->getName() << LL_ENDL;
+
+ renderBadgeBackground(badge_center_x, badge_center_y,
+ badge_width, badge_height,
+ mImageColor % alpha);
+ }
+
+ //
+ // Draw the label
+ //
+
+ mGLFont->render(badge_label_wstring,
+ badge_label_begin_offset,
+ badge_center_x + mLabelOffsetHoriz,
+ badge_center_y + mLabelOffsetVert,
+ mLabelColor % alpha,
+ LLFontGL::HCENTER, LLFontGL::VCENTER, // centered around the position
+ LLFontGL::NORMAL, // normal text (not bold, italics, etc.)
+ LLFontGL::DROP_SHADOW_SOFT,
+ badge_char_length, badge_pixel_length,
+ right_position_out, do_not_use_ellipses);
+ }
+ }
+}
+
+
+namespace LLInitParam
+{
+ void TypeValues<LLRelPos::Location>::declareValues()
+ {
+ declare("bottom", LLRelPos::BOTTOM);
+ declare("bottom_left", LLRelPos::BOTTOM_LEFT);
+ declare("bottom_right", LLRelPos::BOTTOM_RIGHT);
+ declare("center", LLRelPos::CENTER);
+ declare("left", LLRelPos::LEFT);
+ declare("right", LLRelPos::RIGHT);
+ declare("top", LLRelPos::TOP);
+ declare("top_left", LLRelPos::TOP_LEFT);
+ declare("top_right", LLRelPos::TOP_RIGHT);
+ }
+}
+
+
+// eof
diff --git a/indra/llui/llbadge.h b/indra/llui/llbadge.h
new file mode 100755
index 0000000000..4b21a71aaa
--- /dev/null
+++ b/indra/llui/llbadge.h
@@ -0,0 +1,174 @@
+/**
+ * @file llbadge.h
+ * @brief Header for badges
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLBADGE_H
+#define LL_LLBADGE_H
+
+#include <string>
+
+#include "lluicolor.h"
+#include "lluictrl.h"
+#include "llstring.h"
+#include "lluiimage.h"
+#include "llview.h"
+
+//
+// Declarations
+//
+
+class LLFontGL;
+class LLScrollContainer;
+class LLUICtrlFactory;
+
+//
+// Relative Position Alignment
+//
+
+namespace LLRelPos
+{
+ enum Location
+ {
+ CENTER = 0,
+
+ LEFT = (1 << 0),
+ RIGHT = (1 << 1),
+
+ TOP = (1 << 2),
+ BOTTOM = (1 << 3),
+
+ BOTTOM_LEFT = (BOTTOM | LEFT),
+ BOTTOM_RIGHT = (BOTTOM | RIGHT),
+
+ TOP_LEFT = (TOP | LEFT),
+ TOP_RIGHT = (TOP | RIGHT),
+ };
+
+ inline bool IsBottom(Location relPos) { return (relPos & BOTTOM) == BOTTOM; }
+ inline bool IsCenter(Location relPos) { return (relPos == CENTER); }
+ inline bool IsLeft(Location relPos) { return (relPos & LEFT) == LEFT; }
+ inline bool IsRight(Location relPos) { return (relPos & RIGHT) == RIGHT; }
+ inline bool IsTop(Location relPos) { return (relPos & TOP) == TOP; }
+}
+
+// NOTE: This needs to occur before Optional<LLRelPos::Location> declaration for proper compilation.
+namespace LLInitParam
+{
+ template<>
+ struct TypeValues<LLRelPos::Location> : public TypeValuesHelper<LLRelPos::Location>
+ {
+ static void declareValues();
+ };
+}
+
+//
+// Classes
+//
+
+class LLBadge
+: public LLUICtrl
+{
+public:
+ struct Params
+ : public LLInitParam::Block<Params, LLUICtrl::Params>
+ {
+ Optional< LLHandle<LLView> > owner; // Mandatory in code but not in xml
+
+ Optional< LLUIImage* > border_image;
+ Optional< LLUIColor > border_color;
+
+ Optional< LLUIImage* > image;
+ Optional< LLUIColor > image_color;
+
+ Optional< std::string > label;
+ Optional< LLUIColor > label_color;
+
+ Optional< S32 > label_offset_horiz;
+ Optional< S32 > label_offset_vert;
+
+ Optional< LLRelPos::Location > location;
+ Optional< S32 > location_offset_hcenter;
+ Optional< S32 > location_offset_vcenter;
+ Optional< U32 > location_percent_hcenter;
+ Optional< U32 > location_percent_vcenter;
+
+ Optional< F32 > padding_horiz;
+ Optional< F32 > padding_vert;
+
+ Params();
+
+ bool equals(const Params&) const;
+ };
+
+protected:
+ friend class LLUICtrlFactory;
+ LLBadge(const Params& p);
+
+public:
+
+ ~LLBadge();
+
+ bool addToView(LLView * view);
+
+ virtual void draw();
+
+ const std::string getLabel() const { return wstring_to_utf8str(mLabel); }
+ void setLabel( const LLStringExplicit& label);
+
+private:
+ LLPointer< LLUIImage > mBorderImage;
+ LLUIColor mBorderColor;
+
+ const LLFontGL* mGLFont;
+
+ LLPointer< LLUIImage > mImage;
+ LLUIColor mImageColor;
+
+ LLUIString mLabel;
+ LLUIColor mLabelColor;
+
+ S32 mLabelOffsetHoriz;
+ S32 mLabelOffsetVert;
+
+ LLRelPos::Location mLocation;
+ S32 mLocationOffsetHCenter;
+ S32 mLocationOffsetVCenter;
+ F32 mLocationPercentHCenter;
+ F32 mLocationPercentVCenter;
+
+ LLHandle< LLView > mOwner;
+
+ F32 mPaddingHoriz;
+ F32 mPaddingVert;
+
+ LLScrollContainer* mParentScroller;
+};
+
+// Build time optimization, generate once in .cpp file
+#ifndef LLBADGE_CPP
+extern template class LLBadge* LLView::getChild<class LLBadge>(const std::string& name, BOOL recurse) const;
+#endif
+
+#endif // LL_LLBADGE_H
diff --git a/indra/llui/llfunctorregistry.cpp b/indra/llui/llbadgeholder.cpp
index 8003324973..1f786f36ae 100644..100755
--- a/indra/llui/llfunctorregistry.cpp
+++ b/indra/llui/llbadgeholder.cpp
@@ -1,9 +1,8 @@
-/**
- * @file llfunctorregistry.cpp
- * @author Kent Quirk
- * @brief Maintains a registry of named callback functors taking a single LLSD parameter
+/**
+ * @file llbadgeholder.cpp
+ * @brief Source for badge holders
*
- * $LicenseInfo:firstyear=2008&license=viewerlgpl$
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
@@ -23,11 +22,24 @@
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
- **/
+ */
-#include "linden_common.h"
-#include "llfunctorregistry.h"
+#include "llbadgeholder.h"
-// This is a default functor always resident in the system.
-// It's used whenever a functor isn't found in the registry, so that
-// we at least log the data relating to the user response.
+#include "llbadge.h"
+#include "llview.h"
+
+
+bool LLBadgeHolder::addBadge(LLBadge * badge)
+{
+ bool badge_added = false;
+
+ LLView * this_view = dynamic_cast<LLView *>(this);
+
+ if (this_view && mAcceptsBadge)
+ {
+ badge_added = badge->addToView(this_view);
+ }
+
+ return badge_added;
+}
diff --git a/indra/llui/llbadgeholder.h b/indra/llui/llbadgeholder.h
new file mode 100755
index 0000000000..2538eaae91
--- /dev/null
+++ b/indra/llui/llbadgeholder.h
@@ -0,0 +1,56 @@
+/**
+ * @file llbadgeholder.h
+ * @brief Header for badge holders
+ *
+ * $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_LLBADGEHOLDER_H
+#define LL_LLBADGEHOLDER_H
+
+//
+// Classes
+//
+
+class LLBadge;
+
+class LLBadgeHolder
+{
+public:
+
+ LLBadgeHolder(bool acceptsBadge)
+ : mAcceptsBadge(acceptsBadge)
+ {
+ }
+
+ void setAcceptsBadge(bool acceptsBadge) { mAcceptsBadge = acceptsBadge; }
+ bool acceptsBadge() const { return mAcceptsBadge; }
+
+ virtual bool addBadge(LLBadge * badge);
+
+private:
+
+ bool mAcceptsBadge;
+
+};
+
+#endif // LL_LLBADGEHOLDER_H
diff --git a/indra/llui/llbadgeowner.cpp b/indra/llui/llbadgeowner.cpp
new file mode 100755
index 0000000000..55e64bb940
--- /dev/null
+++ b/indra/llui/llbadgeowner.cpp
@@ -0,0 +1,104 @@
+/**
+ * @file llbadgeowner.cpp
+ * @brief Class to manage badges attached to a UI 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$
+ */
+
+#include "linden_common.h"
+
+#include "llbadgeholder.h"
+#include "llbadgeowner.h"
+#include "llpanel.h"
+
+//
+// Classes
+//
+
+LLBadgeOwner::LLBadgeOwner(LLHandle< LLView > viewHandle)
+ : mHasBadgeHolderParent(false),
+ mBadge(NULL),
+ mBadgeOwnerView(viewHandle)
+{
+}
+
+void LLBadgeOwner::initBadgeParams(const LLBadge::Params& p)
+{
+ if (!p.equals(LLUICtrlFactory::getDefaultParams<LLBadge>()))
+ {
+ mBadge = createBadge(p);
+ mHasBadgeHolderParent = false;
+
+ LLView * owner_view = mBadgeOwnerView.get();
+ if (owner_view)
+ {
+ mBadge->addToView(owner_view);
+ }
+ }
+}
+
+void LLBadgeOwner::setBadgeVisibility(bool visible)
+{
+ if (mBadge)
+ {
+ mBadge->setVisible(visible);
+ }
+}
+
+void LLBadgeOwner::addBadgeToParentHolder()
+{
+ LLView * owner_view = mBadgeOwnerView.get();
+
+ if (mBadge && owner_view)
+ {
+ LLBadgeHolder * badge_holder = NULL;
+
+ // Find the appropriate holder for the badge
+ LLView * parent = owner_view->getParent();
+
+ while (parent)
+ {
+ LLBadgeHolder * badge_holder_panel = dynamic_cast<LLBadgeHolder *>(parent);
+
+ if (badge_holder_panel && badge_holder_panel->acceptsBadge())
+ {
+ badge_holder = badge_holder_panel;
+ break;
+ }
+
+ parent = parent->getParent();
+ }
+
+ if (badge_holder)
+ {
+ mHasBadgeHolderParent = badge_holder->addBadge(mBadge);
+ }
+ }
+}
+
+LLBadge* LLBadgeOwner::createBadge(const LLBadge::Params& p)
+{
+ LLBadge::Params badge_params(p);
+ badge_params.owner = mBadgeOwnerView;
+
+ return LLUICtrlFactory::create<LLBadge>(badge_params);
+}
diff --git a/indra/llui/llbadgeowner.h b/indra/llui/llbadgeowner.h
new file mode 100755
index 0000000000..53c2de95c8
--- /dev/null
+++ b/indra/llui/llbadgeowner.h
@@ -0,0 +1,59 @@
+/**
+ * @file llbadgeowner.h
+ * @brief Header for badge owners
+ *
+ * $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_LLBADGEOWNER_H
+#define LL_LLBADGEOWNER_H
+
+#include "llbadge.h"
+#include "llview.h"
+
+//
+// Classes
+//
+
+class LLBadgeOwner
+{
+public:
+
+ LLBadgeOwner(LLHandle< LLView > viewHandle);
+
+ void initBadgeParams(const LLBadge::Params& p);
+ void addBadgeToParentHolder();
+
+ bool hasBadgeHolderParent() const { return mHasBadgeHolderParent; };
+ void setBadgeVisibility(bool visible);
+
+private:
+
+ LLBadge* createBadge(const LLBadge::Params& p);
+
+private:
+ bool mHasBadgeHolderParent;
+ LLBadge* mBadge;
+ LLHandle< LLView > mBadgeOwnerView;
+};
+
+#endif // LL_LLBADGEOWNER_H
diff --git a/indra/llui/llbutton.cpp b/indra/llui/llbutton.cpp
index 45ceaff696..8c7df45884 100644..100755
--- a/indra/llui/llbutton.cpp
+++ b/indra/llui/llbutton.cpp
@@ -49,6 +49,7 @@
#include "lluictrlfactory.h"
#include "llhelp.h"
#include "lldockablefloater.h"
+#include "llviewereventrecorder.h"
static LLDefaultChildRegistry::Register<LLButton> r("button");
@@ -83,10 +84,11 @@ LLButton::Params::Params()
label_color_selected("label_color_selected"), // requires is_toggle true
label_color_disabled("label_color_disabled"),
label_color_disabled_selected("label_color_disabled_selected"),
- highlight_color("highlight_color"),
image_color("image_color"),
image_color_disabled("image_color_disabled"),
- image_overlay_color("image_overlay_color", LLColor4::white),
+ image_overlay_color("image_overlay_color", LLColor4::white % 0.75f),
+ image_overlay_disabled_color("image_overlay_disabled_color", LLColor4::white % 0.3f),
+ image_overlay_selected_color("image_overlay_selected_color", LLColor4::white),
flash_color("flash_color"),
pad_right("pad_right", LLUI::sSettingGroups["config"]->getS32("ButtonHPad")),
pad_left("pad_left", LLUI::sSettingGroups["config"]->getS32("ButtonHPad")),
@@ -99,16 +101,23 @@ LLButton::Params::Params()
scale_image("scale_image", true),
hover_glow_amount("hover_glow_amount"),
commit_on_return("commit_on_return", true),
- use_draw_context_alpha("use_draw_context_alpha", true)
+ display_pressed_state("display_pressed_state", true),
+ use_draw_context_alpha("use_draw_context_alpha", true),
+ badge("badge"),
+ handle_right_mouse("handle_right_mouse"),
+ held_down_delay("held_down_delay"),
+ button_flash_enable("button_flash_enable", false),
+ button_flash_count("button_flash_count"),
+ button_flash_rate("button_flash_rate")
{
addSynonym(is_toggle, "toggle");
- held_down_delay.seconds = 0.5f;
- initial_value.set(LLSD(false), false);
+ changeDefault(initial_value, LLSD(false));
}
LLButton::LLButton(const LLButton::Params& p)
: LLUICtrl(p),
+ LLBadgeOwner(getHandle()),
mMouseDownFrame(0),
mMouseHeldDownCount(0),
mBorderEnabled( FALSE ),
@@ -133,12 +142,13 @@ LLButton::LLButton(const LLButton::Params& p)
mSelectedLabelColor(p.label_color_selected()),
mDisabledLabelColor(p.label_color_disabled()),
mDisabledSelectedLabelColor(p.label_color_disabled_selected()),
- mHighlightColor(p.highlight_color()),
mImageColor(p.image_color()),
mFlashBgColor(p.flash_color()),
mDisabledImageColor(p.image_color_disabled()),
mImageOverlay(p.image_overlay()),
mImageOverlayColor(p.image_overlay_color()),
+ mImageOverlayDisabledColor(p.image_overlay_disabled_color()),
+ mImageOverlaySelectedColor(p.image_overlay_selected_color()),
mImageOverlayAlignment(LLFontGL::hAlignFromName(p.image_overlay_alignment)),
mImageOverlayTopPad(p.image_top_pad),
mImageOverlayBottomPad(p.image_bottom_pad),
@@ -156,13 +166,31 @@ LLButton::LLButton(const LLButton::Params& p)
mCommitOnReturn(p.commit_on_return),
mFadeWhenDisabled(FALSE),
mForcePressedState(false),
+ mDisplayPressedState(p.display_pressed_state),
mLastDrawCharsCount(0),
mMouseDownSignal(NULL),
mMouseUpSignal(NULL),
mHeldDownSignal(NULL),
- mUseDrawContextAlpha(p.use_draw_context_alpha)
-
+ mUseDrawContextAlpha(p.use_draw_context_alpha),
+ mHandleRightMouse(p.handle_right_mouse),
+ mFlashingTimer(NULL)
{
+ if (p.button_flash_enable)
+ {
+ // If optional parameter "p.button_flash_count" is not provided, LLFlashTimer will be
+ // used instead it a "default" value from gSavedSettings.getS32("FlashCount")).
+ // Likewise, missing "p.button_flash_rate" is replaced by gSavedSettings.getF32("FlashPeriod").
+ // Note: flashing should be allowed in settings.xml (boolean key "EnableButtonFlashing").
+ S32 flash_count = p.button_flash_count.isProvided()? p.button_flash_count : 0;
+ F32 flash_rate = p.button_flash_rate.isProvided()? p.button_flash_rate : 0.0;
+ mFlashingTimer = new LLFlashTimer ((LLFlashTimer::callback_t)NULL, flash_count, flash_rate);
+ }
+ else
+ {
+ mButtonFlashCount = p.button_flash_count;
+ mButtonFlashRate = p.button_flash_rate;
+ }
+
static LLUICachedControl<S32> llbutton_orig_h_pad ("UIButtonOrigHPad", 0);
static Params default_params(LLUICtrlFactory::getDefaultParams<LLButton>());
@@ -225,7 +253,7 @@ LLButton::LLButton(const LLButton::Params& p)
if (mImageUnselected.isNull())
{
- llwarns << "Button: " << getName() << " with no image!" << llendl;
+ LL_WARNS() << "Button: " << getName() << " with no image!" << LL_ENDL;
}
if (p.click_callback.isProvided())
@@ -244,6 +272,11 @@ LLButton::LLButton(const LLButton::Params& p)
{
setHeldDownCallback(initCommitCallback(p.mouse_held_callback));
}
+
+ if (p.badge.isProvided())
+ {
+ LLBadgeOwner::initBadgeParams(p.badge());
+ }
}
LLButton::~LLButton()
@@ -251,6 +284,11 @@ LLButton::~LLButton()
delete mMouseDownSignal;
delete mMouseUpSignal;
delete mHeldDownSignal;
+
+ if (mFlashingTimer)
+ {
+ mFlashingTimer->unset();
+ }
}
// HACK: Committing a button is the same as instantly clicking it.
@@ -284,6 +322,24 @@ void LLButton::onCommit()
LLUICtrl::onCommit();
}
+boost::signals2::connection LLButton::setClickedCallback(const CommitCallbackParam& cb)
+{
+ return setClickedCallback(initCommitCallback(cb));
+}
+boost::signals2::connection LLButton::setMouseDownCallback(const CommitCallbackParam& cb)
+{
+ return setMouseDownCallback(initCommitCallback(cb));
+}
+boost::signals2::connection LLButton::setMouseUpCallback(const CommitCallbackParam& cb)
+{
+ return setMouseUpCallback(initCommitCallback(cb));
+}
+boost::signals2::connection LLButton::setHeldDownCallback(const CommitCallbackParam& cb)
+{
+ return setHeldDownCallback(initCommitCallback(cb));
+}
+
+
boost::signals2::connection LLButton::setClickedCallback( const commit_signal_t::slot_type& cb )
{
if (!mCommitSignal) mCommitSignal = new commit_signal_t();
@@ -306,7 +362,7 @@ boost::signals2::connection LLButton::setHeldDownCallback( const commit_signal_t
}
-// *TODO: Deprecate (for backwards compatability only)
+// *TODO: Deprecate (for backwards compatibility only)
boost::signals2::connection LLButton::setClickedCallback( button_callback_t cb, void* data )
{
return setClickedCallback(boost::bind(cb, data));
@@ -327,8 +383,12 @@ boost::signals2::connection LLButton::setHeldDownCallback( button_callback_t cb,
BOOL LLButton::postBuild()
{
autoResize();
- return TRUE;
+
+ addBadgeToParentHolder();
+
+ return LLUICtrl::postBuild();
}
+
BOOL LLButton::handleUnicodeCharHere(llwchar uni_char)
{
BOOL handled = FALSE;
@@ -384,6 +444,8 @@ BOOL LLButton::handleMouseDown(S32 x, S32 y, MASK mask)
*/
LLUICtrl::handleMouseDown(x, y, mask);
+ LLViewerEventRecorder::instance().updateMouseEventInfo(x,y,-55,-55,getPathname());
+
if(mMouseDownSignal) (*mMouseDownSignal)(this, LLSD());
mMouseDownTimer.start();
@@ -414,6 +476,7 @@ BOOL LLButton::handleMouseUp(S32 x, S32 y, MASK mask)
* by calling LLUICtrl::mMouseUpSignal(x, y, mask);
*/
LLUICtrl::handleMouseUp(x, y, mask);
+ LLViewerEventRecorder::instance().updateMouseEventInfo(x,y,-55,-55,getPathname());
// Regardless of where mouseup occurs, handle callback
if(mMouseUpSignal) (*mMouseUpSignal)(this, LLSD());
@@ -447,7 +510,7 @@ BOOL LLButton::handleMouseUp(S32 x, S32 y, MASK mask)
BOOL LLButton::handleRightMouseDown(S32 x, S32 y, MASK mask)
{
- if (!childrenHandleRightMouseDown(x, y, mask))
+ if (mHandleRightMouse && !childrenHandleRightMouseDown(x, y, mask))
{
// Route future Mouse messages here preemptively. (Release on mouse up.)
gFocusMgr.setMouseCapture( this );
@@ -460,49 +523,45 @@ BOOL LLButton::handleRightMouseDown(S32 x, S32 y, MASK mask)
// 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.
}
- // send the mouse down signal
- LLUICtrl::handleRightMouseDown(x,y,mask);
- // *TODO: Return result of LLUICtrl call above? Should defer to base class
- // but this might change the mouse handling of existing buttons in a bad way
- // if they are not mouse opaque.
+
return TRUE;
}
BOOL LLButton::handleRightMouseUp(S32 x, S32 y, MASK mask)
{
- // We only handle the click if the click both started and ended within us
- if( hasMouseCapture() )
+ if (mHandleRightMouse)
{
- // Always release the mouse
- gFocusMgr.setMouseCapture( NULL );
+ // We only handle the click if the click both started and ended within us
+ if( hasMouseCapture() )
+ {
+ // Always release the mouse
+ gFocusMgr.setMouseCapture( NULL );
-// if (pointInView(x, y))
-// {
-// mRightMouseUpSignal(this, x,y,mask);
-// }
- }
- else
- {
- childrenHandleRightMouseUp(x, y, mask);
+ // 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.
}
- // 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)
-{
- LLUICtrl::onMouseEnter(x, y, mask);
-
- if (isInEnabledChain())
- mNeedsHighlight = TRUE;
-}
-
void LLButton::onMouseLeave(S32 x, S32 y, MASK mask)
{
LLUICtrl::onMouseLeave(x, y, mask);
@@ -517,6 +576,10 @@ void LLButton::setHighlight(bool b)
BOOL LLButton::handleHover(S32 x, S32 y, MASK mask)
{
+ if (isInEnabledChain()
+ && (!gFocusMgr.getMouseCapture() || gFocusMgr.getMouseCapture() == this))
+ mNeedsHighlight = TRUE;
+
if (!childrenHandleHover(x, y, mask))
{
if (mMouseDownTimer.getStarted())
@@ -532,27 +595,27 @@ BOOL LLButton::handleHover(S32 x, S32 y, MASK mask)
// 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;
+ LL_DEBUGS("UserInput") << "hover handled by " << getName() << LL_ENDL;
}
return TRUE;
}
+void LLButton::getOverlayImageSize(S32& overlay_width, S32& overlay_height)
+{
+ overlay_width = mImageOverlay->getWidth();
+ overlay_height = mImageOverlay->getHeight();
+
+ F32 scale_factor = llmin((F32)getRect().getWidth() / (F32)overlay_width, (F32)getRect().getHeight() / (F32)overlay_height, 1.f);
+ overlay_width = ll_round((F32)overlay_width * scale_factor);
+ overlay_height = ll_round((F32)overlay_height * scale_factor);
+}
+
// virtual
void LLButton::draw()
{
+ static LLCachedControl<bool> sEnableButtonFlashing(*LLUI::sSettingGroups["config"], "EnableButtonFlashing", true);
F32 alpha = mUseDrawContextAlpha ? getDrawContext().mAlpha : getCurrentTransparency();
- bool flash = FALSE;
- static LLUICachedControl<F32> button_flash_rate("ButtonFlashRate", 0);
- static LLUICachedControl<S32> button_flash_count("ButtonFlashCount", 0);
-
- if( mFlashing )
- {
- F32 elapsed = mFlashingTimer.getElapsedTimeF32();
- S32 flash_count = S32(elapsed * button_flash_rate * 2.f);
- // flash on or off?
- flash = (flash_count % 2 == 0) || flash_count > S32((F32)button_flash_count * 2.f);
- }
bool pressed_by_keyboard = FALSE;
if (hasFocus())
@@ -577,10 +640,22 @@ void LLButton::draw()
bool selected = getToggleState();
bool use_glow_effect = FALSE;
+ LLColor4 highlighting_color = LLColor4::white;
LLColor4 glow_color = LLColor4::white;
LLRender::eBlendType glow_type = LLRender::BT_ADD_WITH_ALPHA;
LLUIImage* imagep = NULL;
- if (pressed)
+
+ // Cancel sticking of color, if the button is pressed,
+ // or when a flashing of the previously selected button is ended
+ if (mFlashingTimer
+ && ((selected && !mFlashingTimer->isFlashingInProgress() && !mForceFlashing) || pressed))
+ {
+ mFlashing = false;
+ }
+
+ bool flash = mFlashing && sEnableButtonFlashing;
+
+ if (pressed && mDisplayPressedState)
{
imagep = selected ? mImagePressedSelected : mImagePressed;
}
@@ -645,15 +720,20 @@ void LLButton::draw()
imagep = mImageFlash;
}
// else use usual flashing via flash_color
- else
+ else if (mFlashingTimer)
{
LLColor4 flash_color = mFlashBgColor.get();
use_glow_effect = TRUE;
glow_type = LLRender::BT_ALPHA; // blend the glow
- if (mNeedsHighlight) // highlighted AND flashing
- glow_color = (glow_color*0.5f + flash_color*0.5f) % 2.0f; // average between flash and highlight colour, with sum of the opacity
- else
+
+ if (mFlashingTimer->isCurrentlyHighlighted() || !mFlashingTimer->isFlashingInProgress())
+ {
glow_color = flash_color;
+ }
+ else if (mNeedsHighlight)
+ {
+ glow_color = highlighting_color;
+ }
}
}
@@ -690,34 +770,24 @@ void LLButton::draw()
}
// Unselected label assignments
- LLWString label;
-
- if( getToggleState() )
- {
- label = mSelectedLabel;
- }
- else
- {
- label = mUnselectedLabel;
- }
+ LLWString label = getCurrentLabel();
// overlay with keyboard focus border
if (hasFocus())
{
F32 lerp_amt = gFocusMgr.getFocusFlashAmt();
- drawBorder(imagep, gFocusMgr.getFocusColor() % alpha, llround(lerp(1.f, 3.f, lerp_amt)));
+ drawBorder(imagep, gFocusMgr.getFocusColor() % alpha, ll_round(lerp(1.f, 3.f, lerp_amt)));
}
if (use_glow_effect)
{
mCurGlowStrength = lerp(mCurGlowStrength,
- mFlashing ? (flash? 1.0 : 0.0)
- : mHoverGlowStrength,
- LLCriticalDamp::getInterpolant(0.05f));
+ mFlashing ? (mFlashingTimer->isCurrentlyHighlighted() || !mFlashingTimer->isFlashingInProgress() || mNeedsHighlight? 1.f : 0.f) : mHoverGlowStrength,
+ LLSmoothInterpolation::getInterpolant(0.05f));
}
else
{
- mCurGlowStrength = lerp(mCurGlowStrength, 0.f, LLCriticalDamp::getInterpolant(0.05f));
+ mCurGlowStrength = lerp(mCurGlowStrength, 0.f, LLSmoothInterpolation::getInterpolant(0.05f));
}
// Draw button image, if available.
@@ -750,7 +820,7 @@ void LLButton::draw()
else
{
// no image
- lldebugs << "No image for button " << getName() << llendl;
+ LL_DEBUGS() << "No image for button " << getName() << LL_ENDL;
// draw it in pink so we can find it
gl_rect_2d(0, getRect().getHeight(), getRect().getWidth(), 0, LLColor4::pink1 % alpha, FALSE);
}
@@ -764,18 +834,16 @@ void LLButton::draw()
if (mImageOverlay.notNull())
{
// get max width and height (discard level 0)
- S32 overlay_width = mImageOverlay->getWidth();
- S32 overlay_height = mImageOverlay->getHeight();
+ S32 overlay_width;
+ S32 overlay_height;
- F32 scale_factor = llmin((F32)getRect().getWidth() / (F32)overlay_width, (F32)getRect().getHeight() / (F32)overlay_height, 1.f);
- overlay_width = llround((F32)overlay_width * scale_factor);
- overlay_height = llround((F32)overlay_height * scale_factor);
+ getOverlayImageSize(overlay_width, overlay_height);
S32 center_x = getLocalRect().getCenterX();
S32 center_y = getLocalRect().getCenterY();
//FUGLY HACK FOR "DEPRESSED" BUTTONS
- if (pressed)
+ if (pressed && mDisplayPressedState)
{
center_y--;
center_x++;
@@ -786,7 +854,11 @@ void LLButton::draw()
LLColor4 overlay_color = mImageOverlayColor.get();
if (!enabled)
{
- overlay_color.mV[VALPHA] = 0.5f;
+ overlay_color = mImageOverlayDisabledColor.get();
+ }
+ else if (getToggleState())
+ {
+ overlay_color = mImageOverlaySelectedColor.get();
}
overlay_color.mV[VALPHA] *= alpha;
@@ -794,6 +866,7 @@ void LLButton::draw()
{
case LLFontGL::LEFT:
text_left += overlay_width + mImgOverlayLabelSpace;
+ text_width -= overlay_width + mImgOverlayLabelSpace;
mImageOverlay->draw(
mLeftHPad,
center_y - (overlay_height / 2),
@@ -811,6 +884,7 @@ void LLButton::draw()
break;
case LLFontGL::RIGHT:
text_right -= overlay_width + mImgOverlayLabelSpace;
+ text_width -= overlay_width + mImgOverlayLabelSpace;
mImageOverlay->draw(
getRect().getWidth() - mRightHPad - overlay_width,
center_y - (overlay_height / 2),
@@ -846,7 +920,7 @@ void LLButton::draw()
S32 y_offset = 2 + (getRect().getHeight() - 20)/2;
- if (pressed)
+ if (pressed && mDisplayPressedState)
{
y_offset--;
x++;
@@ -859,9 +933,9 @@ void LLButton::draw()
// 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),
+ (F32)(getRect().getHeight() / 2 + mBottomVPad),
label_color % alpha,
- mHAlign, LLFontGL::BOTTOM,
+ mHAlign, LLFontGL::VCENTER,
LLFontGL::NORMAL,
mDropShadowedText ? LLFontGL::DROP_SHADOW_SOFT : LLFontGL::NO_SHADOW,
S32_MAX, text_width,
@@ -895,21 +969,27 @@ void LLButton::setToggleState(BOOL b)
{
setControlValue(b); // will fire LLControlVariable callbacks (if any)
setValue(b); // may or may not be redundant
+ setFlashing(false); // stop flash state whenever the selected/unselected state if reset
// Unselected label assignments
autoResize();
}
}
-void LLButton::setFlashing( BOOL b )
+void LLButton::setFlashing(bool b, bool force_flashing/* = false */)
{
- if (b != mFlashing)
+ mForceFlashing = force_flashing;
+ if (mFlashingTimer)
{
mFlashing = b;
- mFlashingTimer.reset();
+ (b ? mFlashingTimer->startFlashing() : mFlashingTimer->stopFlashing());
+ }
+ else if (b != mFlashing)
+ {
+ mFlashing = b;
+ mFrameTimer.reset();
}
}
-
BOOL LLButton::toggleState()
{
bool flipped = ! getToggleState();
@@ -942,27 +1022,35 @@ void LLButton::setLabelSelected( const LLStringExplicit& label )
mSelectedLabel = label;
}
+bool LLButton::labelIsTruncated() const
+{
+ return getCurrentLabel().getString().size() > mLastDrawCharsCount;
+}
+
+const LLUIString& LLButton::getCurrentLabel() const
+{
+ if( getToggleState() )
+ {
+ return mSelectedLabel;
+ }
+ else
+ {
+ return mUnselectedLabel;
+ }
+}
+
void LLButton::setImageUnselected(LLPointer<LLUIImage> image)
{
mImageUnselected = image;
if (mImageUnselected.isNull())
{
- llwarns << "Setting default button image for: " << getName() << " to NULL" << llendl;
+ LL_WARNS() << "Setting default button image for: " << getName() << " to NULL" << LL_ENDL;
}
}
void LLButton::autoResize()
{
- LLUIString label;
- if(getToggleState())
- {
- label = mSelectedLabel;
- }
- else
- {
- label = mUnselectedLabel;
- }
- resize(label);
+ resize(getCurrentLabel());
}
void LLButton::resize(LLUIString label)
@@ -972,11 +1060,32 @@ void LLButton::resize(LLUIString label)
// get current btn length
S32 btn_width =getRect().getWidth();
// check if it need resize
- if (mAutoResize == TRUE)
+ if (mAutoResize)
{
- if (btn_width - (mRightHPad + mLeftHPad) < label_width)
+ S32 min_width = label_width + mLeftHPad + mRightHPad;
+ if (mImageOverlay)
{
- setRect(LLRect( getRect().mLeft, getRect().mTop, getRect().mLeft + label_width + mLeftHPad + mRightHPad , getRect().mBottom));
+ S32 overlay_width = mImageOverlay->getWidth();
+ F32 scale_factor = (getRect().getHeight() - (mImageOverlayBottomPad + mImageOverlayTopPad)) / (F32)mImageOverlay->getHeight();
+ overlay_width = ll_round((F32)overlay_width * scale_factor);
+
+ switch(mImageOverlayAlignment)
+ {
+ case LLFontGL::LEFT:
+ case LLFontGL::RIGHT:
+ min_width += overlay_width + mImgOverlayLabelSpace;
+ break;
+ case LLFontGL::HCENTER:
+ min_width = llmax(min_width, overlay_width + mLeftHPad + mRightHPad);
+ break;
+ default:
+ // draw nothing
+ break;
+ }
+ }
+ if (btn_width < min_width)
+ {
+ reshape(min_width, getRect().getHeight());
}
}
}
@@ -1123,7 +1232,7 @@ void LLButton::setFloaterToggle(LLUICtrl* ctrl, const LLSD& sdname)
// Set the button control value (toggle state) to the floater visibility control (Sets the value as well)
button->setControlVariable(LLFloater::getControlGroup()->getControl(vis_control_name));
// Set the clicked callback to toggle the floater
- button->setClickedCallback(boost::bind(&LLFloaterReg::toggleFloaterInstance, sdname));
+ button->setClickedCallback(boost::bind(&LLFloaterReg::toggleInstance, sdname, LLSD()));
}
// static
@@ -1164,7 +1273,6 @@ void LLButton::resetMouseDownTimer()
mMouseDownTimer.reset();
}
-
BOOL LLButton::handleDoubleClick(S32 x, S32 y, MASK mask)
{
// just treat a double click as a second click
diff --git a/indra/llui/llbutton.h b/indra/llui/llbutton.h
index 16aa49b653..7b4719866d 100644..100755
--- a/indra/llui/llbutton.h
+++ b/indra/llui/llbutton.h
@@ -28,7 +28,9 @@
#define LL_LLBUTTON_H
#include "lluuid.h"
+#include "llbadgeowner.h"
#include "llcontrol.h"
+#include "llflashtimer.h"
#include "lluictrl.h"
#include "v4color.h"
#include "llframetimer.h"
@@ -59,7 +61,7 @@ class LLUICtrlFactory;
//
class LLButton
-: public LLUICtrl
+: public LLUICtrl, public LLBadgeOwner
{
public:
struct Params
@@ -90,10 +92,11 @@ public:
label_color_selected,
label_color_disabled,
label_color_disabled_selected,
- highlight_color,
image_color,
image_color_disabled,
image_overlay_color,
+ image_overlay_selected_color,
+ image_overlay_disabled_color,
flash_color;
// layout
@@ -119,12 +122,21 @@ public:
// misc
Optional<bool> is_toggle,
scale_image,
- commit_on_return;
+ commit_on_return,
+ display_pressed_state;
Optional<F32> hover_glow_amount;
Optional<TimeIntervalParam> held_down_delay;
- Optional<bool> use_draw_context_alpha;
+ Optional<bool> use_draw_context_alpha;
+
+ Optional<LLBadge::Params> badge;
+
+ Optional<bool> handle_right_mouse;
+
+ Optional<bool> button_flash_enable;
+ Optional<S32> button_flash_count;
+ Optional<F32> button_flash_rate;
Params();
};
@@ -152,7 +164,6 @@ public:
virtual void draw();
/*virtual*/ BOOL postBuild();
- virtual void onMouseEnter(S32 x, S32 y, MASK mask);
virtual void onMouseLeave(S32 x, S32 y, MASK mask);
virtual void onMouseCaptureLost();
@@ -163,6 +174,11 @@ public:
void setUseEllipses( BOOL use_ellipses ) { mUseEllipses = use_ellipses; }
+ boost::signals2::connection setClickedCallback(const CommitCallbackParam& cb);
+ boost::signals2::connection setMouseDownCallback(const CommitCallbackParam& cb);
+ boost::signals2::connection setMouseUpCallback(const CommitCallbackParam& cb);
+ boost::signals2::connection setHeldDownCallback(const CommitCallbackParam& cb);
+
boost::signals2::connection setClickedCallback( const commit_signal_t::slot_type& cb ); // mouse down and up within button
boost::signals2::connection setMouseDownCallback( const commit_signal_t::slot_type& cb );
boost::signals2::connection setMouseUpCallback( const commit_signal_t::slot_type& cb ); // mouse up, EVEN IF NOT IN BUTTON
@@ -185,8 +201,9 @@ public:
void setToggleState(BOOL b);
void setHighlight(bool b);
- void setFlashing( BOOL b );
+ void setFlashing( bool b, bool force_flashing = false );
BOOL getFlashing() const { return mFlashing; }
+ LLFlashTimer* getFlashTimer() {return mFlashingTimer;}
void setHAlign( LLFontGL::HAlign align ) { mHAlign = align; }
LLFontGL::HAlign getHAlign() const { return mHAlign; }
@@ -230,6 +247,8 @@ public:
S32 getLastDrawCharsCount() const { return mLastDrawCharsCount; }
+ bool labelIsTruncated() const;
+ const LLUIString& getCurrentLabel() const;
void setScaleImage(BOOL scale) { mScaleImage = scale; }
BOOL getScaleImage() const { return mScaleImage; }
@@ -248,7 +267,7 @@ public:
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; }
@@ -265,14 +284,16 @@ public:
protected:
LLPointer<LLUIImage> getImageUnselected() const { return mImageUnselected; }
LLPointer<LLUIImage> getImageSelected() const { return mImageSelected; }
+ void getOverlayImageSize(S32& overlay_width, S32& overlay_height);
LLFrameTimer mMouseDownTimer;
+ bool mNeedsHighlight;
+ S32 mButtonFlashCount;
+ F32 mButtonFlashRate;
-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;
@@ -288,6 +309,8 @@ private:
LLPointer<LLUIImage> mImageOverlay;
LLFontGL::HAlign mImageOverlayAlignment;
LLUIColor mImageOverlayColor;
+ LLUIColor mImageOverlaySelectedColor;
+ LLUIColor mImageOverlayDisabledColor;
LLPointer<LLUIImage> mImageUnselected;
LLUIString mUnselectedLabel;
@@ -316,21 +339,19 @@ private:
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 mImageColor;
LLUIColor mDisabledImageColor;
- BOOL mIsToggle;
- BOOL mScaleImage;
-
- BOOL mDropShadowedText;
- BOOL mAutoResize;
- BOOL mUseEllipses;
- BOOL mBorderEnabled;
+ bool mIsToggle;
+ bool mScaleImage;
- BOOL mFlashing;
+ bool mDropShadowedText;
+ bool mAutoResize;
+ bool mUseEllipses;
+ bool mBorderEnabled;
+ bool mFlashing;
LLFontGL::HAlign mHAlign;
S32 mLeftHPad;
@@ -350,12 +371,15 @@ private:
F32 mHoverGlowStrength;
F32 mCurGlowStrength;
- BOOL mNeedsHighlight;
- BOOL mCommitOnReturn;
- BOOL mFadeWhenDisabled;
+ bool mCommitOnReturn;
+ bool mFadeWhenDisabled;
bool mForcePressedState;
+ bool mDisplayPressedState;
- LLFrameTimer mFlashingTimer;
+ LLFrameTimer mFrameTimer;
+ LLFlashTimer * mFlashingTimer;
+ bool mForceFlashing; // Stick flashing color even if button is pressed
+ bool mHandleRightMouse;
};
// Build time optimization, generate once in .cpp file
diff --git a/indra/llui/llcallbackmap.h b/indra/llui/llcallbackmap.h
index 0a10877c09..0a10877c09 100644..100755
--- a/indra/llui/llcallbackmap.h
+++ b/indra/llui/llcallbackmap.h
diff --git a/indra/llui/llchat.h b/indra/llui/llchat.h
new file mode 100755
index 0000000000..f5b242fdfc
--- /dev/null
+++ b/indra/llui/llchat.h
@@ -0,0 +1,109 @@
+/**
+ * @file llchat.h
+ * @author James Cook
+ * @brief Chat constants and data structures.
+ *
+ * $LicenseInfo:firstyear=2006&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLCHAT_H
+#define LL_LLCHAT_H
+
+#include "lluuid.h"
+#include "v3math.h"
+
+// enumerations used by the chat system
+typedef enum e_chat_source_type
+{
+ CHAT_SOURCE_SYSTEM = 0,
+ CHAT_SOURCE_AGENT = 1,
+ CHAT_SOURCE_OBJECT = 2,
+ CHAT_SOURCE_UNKNOWN = 3
+} EChatSourceType;
+
+typedef enum e_chat_type
+{
+ CHAT_TYPE_WHISPER = 0,
+ CHAT_TYPE_NORMAL = 1,
+ CHAT_TYPE_SHOUT = 2,
+ CHAT_TYPE_START = 4,
+ CHAT_TYPE_STOP = 5,
+ CHAT_TYPE_DEBUG_MSG = 6,
+ CHAT_TYPE_REGION = 7,
+ CHAT_TYPE_OWNER = 8,
+ CHAT_TYPE_DIRECT = 9 // From llRegionSayTo()
+} EChatType;
+
+typedef enum e_chat_audible_level
+{
+ CHAT_AUDIBLE_NOT = -1,
+ CHAT_AUDIBLE_BARELY = 0,
+ CHAT_AUDIBLE_FULLY = 1
+} EChatAudible;
+
+typedef enum e_chat_style
+{
+ CHAT_STYLE_NORMAL,
+ CHAT_STYLE_IRC,
+ CHAT_STYLE_HISTORY
+}EChatStyle;
+
+// A piece of chat
+class LLChat
+{
+public:
+ LLChat(const std::string& text = std::string())
+ : mText(text),
+ mFromName(),
+ mFromID(),
+ mNotifId(),
+ mOwnerID(),
+ mSourceType(CHAT_SOURCE_AGENT),
+ mChatType(CHAT_TYPE_NORMAL),
+ mAudible(CHAT_AUDIBLE_FULLY),
+ mMuted(FALSE),
+ mTime(0.0),
+ mTimeStr(),
+ mPosAgent(),
+ mURL(),
+ mChatStyle(CHAT_STYLE_NORMAL),
+ mSessionID()
+ { }
+
+ std::string mText; // UTF-8 line of text
+ std::string mFromName; // agent or object name
+ LLUUID mFromID; // agent id or object id
+ LLUUID mNotifId;
+ LLUUID mOwnerID;
+ EChatSourceType mSourceType;
+ EChatType mChatType;
+ EChatAudible mAudible;
+ BOOL mMuted; // pass muted chat to maintain list of chatters
+ F64 mTime; // viewer only, seconds from viewer start
+ std::string mTimeStr;
+ LLVector3 mPosAgent;
+ std::string mURL;
+ EChatStyle mChatStyle;
+ LLUUID mSessionID;
+};
+
+#endif
diff --git a/indra/llui/llchatentry.cpp b/indra/llui/llchatentry.cpp
new file mode 100755
index 0000000000..dac001afab
--- /dev/null
+++ b/indra/llui/llchatentry.cpp
@@ -0,0 +1,251 @@
+/**
+ * @file llchatentry.cpp
+ * @brief LLChatEntry implementation
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2012, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+#include "llscrollcontainer.h"
+
+#include "llchatentry.h"
+
+static LLDefaultChildRegistry::Register<LLChatEntry> r("chat_editor");
+
+LLChatEntry::Params::Params()
+: has_history("has_history", true),
+ is_expandable("is_expandable", false),
+ expand_lines_count("expand_lines_count", 1)
+{}
+
+LLChatEntry::LLChatEntry(const Params& p)
+: LLTextEditor(p),
+ mTextExpandedSignal(NULL),
+ mHasHistory(p.has_history),
+ mIsExpandable(p.is_expandable),
+ mExpandLinesCount(p.expand_lines_count),
+ mPrevLinesCount(0),
+ mSingleLineMode(false),
+ mPrevExpandedLineCount(S32_MAX)
+{
+ // Initialize current history line iterator
+ mCurrentHistoryLine = mLineHistory.begin();
+
+ mAutoIndent = false;
+ keepSelectionOnReturn(true);
+}
+
+LLChatEntry::~LLChatEntry()
+{
+ delete mTextExpandedSignal;
+}
+
+void LLChatEntry::draw()
+{
+ if(mIsExpandable)
+ {
+ reflow();
+ expandText();
+ }
+ LLTextEditor::draw();
+}
+
+void LLChatEntry::onCommit()
+{
+ updateHistory();
+ LLTextEditor::onCommit();
+}
+
+boost::signals2::connection LLChatEntry::setTextExpandedCallback(const commit_signal_t::slot_type& cb)
+{
+ if (!mTextExpandedSignal)
+ {
+ mTextExpandedSignal = new commit_signal_t();
+ }
+ return mTextExpandedSignal->connect(cb);
+}
+
+void LLChatEntry::expandText()
+{
+ S32 line_count = mSingleLineMode ? 1 : mExpandLinesCount;
+
+ int visible_lines_count = llabs(getVisibleLines(true).first - getVisibleLines(true).second);
+ bool can_changed = getLineCount() <= line_count || line_count < mPrevExpandedLineCount ;
+ mPrevExpandedLineCount = line_count;
+
+ // true if pasted text has more lines than expand height limit and expand limit is not reached yet
+ bool text_pasted = (getLineCount() > line_count) && (visible_lines_count < line_count);
+
+ if (mIsExpandable && (can_changed || text_pasted || mSingleLineMode) && getLineCount() != mPrevLinesCount)
+ {
+ int lines_height = 0;
+ if (text_pasted)
+ {
+ // text is pasted and now mLineInfoList.size() > mExpandLineCounts and mLineInfoList is not empty,
+ // so lines_height is the sum of the last 'expanded_line_count' lines height
+ lines_height = (mLineInfoList.end() - line_count)->mRect.mTop - mLineInfoList.back().mRect.mBottom;
+ }
+ else
+ {
+ lines_height = mLineInfoList.begin()->mRect.mTop - mLineInfoList.back().mRect.mBottom;
+ }
+
+ int height = mVPad * 2 + lines_height;
+
+ LLRect doc_rect = getRect();
+ doc_rect.setOriginAndSize(doc_rect.mLeft, doc_rect.mBottom, doc_rect.getWidth(), height);
+ setShape(doc_rect);
+
+ mPrevLinesCount = getLineCount();
+
+ if (mTextExpandedSignal)
+ {
+ (*mTextExpandedSignal)(this, LLSD() );
+ }
+
+ needsReflow();
+ }
+}
+
+// line history support
+void LLChatEntry::updateHistory()
+{
+ // On history enabled, remember committed line and
+ // reset current history line number.
+ // Be sure only to remember lines that are not empty and that are
+ // different from the last on the list.
+ if (mHasHistory && getLength())
+ {
+ // Add text to history, ignoring duplicates
+ if (mLineHistory.empty() || getText() != mLineHistory.back())
+ {
+ mLineHistory.push_back(getText());
+ }
+
+ mCurrentHistoryLine = mLineHistory.end();
+ }
+}
+
+void LLChatEntry::beforeValueChange()
+{
+ if(this->getLength() == 0 && !mLabel.empty())
+ {
+ this->clearSegments();
+ }
+}
+
+void LLChatEntry::onValueChange(S32 start, S32 end)
+{
+ //Internally resetLabel() must meet a condition before it can reset the label
+ resetLabel();
+}
+
+bool LLChatEntry::useLabel() const
+{
+ return !getLength() && !mLabel.empty();
+}
+
+void LLChatEntry::onFocusReceived()
+{
+ LLUICtrl::onFocusReceived();
+ updateAllowingLanguageInput();
+}
+
+void LLChatEntry::onFocusLost()
+{
+ LLTextEditor::focusLostHelper();
+ LLUICtrl::onFocusLost();
+}
+
+BOOL LLChatEntry::handleSpecialKey(const KEY key, const MASK mask)
+{
+ BOOL handled = FALSE;
+
+ LLTextEditor::handleSpecialKey(key, mask);
+
+ switch(key)
+ {
+ case KEY_RETURN:
+ if (MASK_NONE == mask)
+ {
+ needsReflow();
+ }
+ break;
+
+ case KEY_UP:
+ if (mHasHistory && MASK_CONTROL == mask)
+ {
+ if (!mLineHistory.empty() && mCurrentHistoryLine > mLineHistory.begin())
+ {
+ setText(*(--mCurrentHistoryLine));
+ endOfDoc();
+ }
+ else
+ {
+ LLUI::reportBadKeystroke();
+ }
+ handled = TRUE;
+ }
+ break;
+
+ case KEY_DOWN:
+ if (mHasHistory && MASK_CONTROL == mask)
+ {
+ if (!mLineHistory.empty() && mCurrentHistoryLine < (mLineHistory.end() - 1) )
+ {
+ setText(*(++mCurrentHistoryLine));
+ endOfDoc();
+ }
+ else if (!mLineHistory.empty() && mCurrentHistoryLine == (mLineHistory.end() - 1) )
+ {
+ mCurrentHistoryLine++;
+ std::string empty("");
+ setText(empty);
+ needsReflow();
+ endOfDoc();
+ }
+ else
+ {
+ LLUI::reportBadKeystroke();
+ }
+ handled = TRUE;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return handled;
+}
+
+void LLChatEntry::enableSingleLineMode(bool single_line_mode)
+{
+ if (mScroller)
+ {
+ mScroller->setSize(single_line_mode ? 0 : -1);
+ }
+
+ mSingleLineMode = single_line_mode;
+ mPrevLinesCount = -1;
+ setWordWrap(!single_line_mode);
+}
diff --git a/indra/llui/llchatentry.h b/indra/llui/llchatentry.h
new file mode 100755
index 0000000000..3f13691a30
--- /dev/null
+++ b/indra/llui/llchatentry.h
@@ -0,0 +1,106 @@
+/**
+ * @file llchatentry.h
+ * @author Paul Guslisty
+ * @brief Text editor widget which is used for user input
+ *
+ * Features:
+ * Optional line history so previous entries can be recalled by CTRL UP/DOWN
+ * Optional auto-resize behavior on input chat field
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2012, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LLCHATENTRY_H_
+#define LLCHATENTRY_H_
+
+#include "lltexteditor.h"
+
+class LLChatEntry : public LLTextEditor
+{
+public:
+
+ struct Params : public LLInitParam::Block<Params, LLTextEditor::Params>
+ {
+ Optional<bool> has_history,
+ is_expandable;
+
+ Optional<int> expand_lines_count;
+
+ Params();
+ };
+
+ virtual ~LLChatEntry();
+
+protected:
+
+ friend class LLUICtrlFactory;
+ LLChatEntry(const Params& p);
+ /*virtual*/ void beforeValueChange();
+ /*virtual*/ void onValueChange(S32 start, S32 end);
+ /*virtual*/ bool useLabel() const;
+
+public:
+
+ virtual void draw();
+ virtual void onCommit();
+ /*virtual*/ void onFocusReceived();
+ /*virtual*/ void onFocusLost();
+
+ void enableSingleLineMode(bool single_line_mode);
+ boost::signals2::connection setTextExpandedCallback(const commit_signal_t::slot_type& cb);
+
+private:
+
+ /**
+ * Implements auto-resize behavior.
+ * When user's typing reaches the right edge of the chat field
+ * the chat field expands vertically by one line. The bottom of
+ * the chat field remains bottom-justified. The chat field does
+ * not expand beyond mExpandLinesCount.
+ */
+ void expandText();
+
+ /**
+ * Implements line history so previous entries can be recalled by CTRL UP/DOWN
+ */
+ void updateHistory();
+
+ BOOL handleSpecialKey(const KEY key, const MASK mask);
+
+
+ // Fired when text height expanded to mExpandLinesCount
+ commit_signal_t* mTextExpandedSignal;
+
+ // line history support:
+ typedef std::vector<std::string> line_history_t;
+ line_history_t::iterator mCurrentHistoryLine; // currently browsed history line
+ line_history_t mLineHistory; // line history storage
+ bool mHasHistory; // flag for enabled/disabled line history
+ bool mIsExpandable;
+ bool mSingleLineMode;
+
+ S32 mExpandLinesCount;
+ S32 mPrevLinesCount;
+ S32 mPrevExpandedLineCount;
+};
+
+#endif /* LLCHATENTRY_H_ */
diff --git a/indra/llui/llcheckboxctrl.cpp b/indra/llui/llcheckboxctrl.cpp
index 4fe444c1a4..eee6339caf 100644..100755
--- a/indra/llui/llcheckboxctrl.cpp
+++ b/indra/llui/llcheckboxctrl.cpp
@@ -41,8 +41,6 @@
#include "lltextbox.h"
#include "llkeyboard.h"
-const U32 MAX_STRING_LENGTH = 10;
-
static LLDefaultChildRegistry::Register<LLCheckBoxCtrl> r("check_box");
// Compiler optimization, generate extern template
@@ -107,7 +105,7 @@ LLCheckBoxCtrl::LLCheckBoxCtrl(const LLCheckBoxCtrl::Params& p)
LLButton::Params params = p.check_button;
params.rect(btn_rect);
//params.control_name(p.control_name);
- params.click_callback.function(boost::bind(&LLCheckBoxCtrl::onButtonPress, this, _2));
+ params.click_callback.function(boost::bind(&LLCheckBoxCtrl::onCommit, this));
params.commit_on_return(false);
// Checkboxes only allow boolean initial values, but buttons can
// take any LLSD.
@@ -123,18 +121,6 @@ LLCheckBoxCtrl::~LLCheckBoxCtrl()
// Children all cleaned up by default view destructor.
}
-
-// static
-void LLCheckBoxCtrl::onButtonPress( const LLSD& data )
-{
- //if (mRadioStyle)
- //{
- // setValue(TRUE);
- //}
-
- onCommit();
-}
-
void LLCheckBoxCtrl::onCommit()
{
if( getEnabled() )
diff --git a/indra/llui/llcheckboxctrl.h b/indra/llui/llcheckboxctrl.h
index 67d8091a97..5ce45b2135 100644..100755
--- a/indra/llui/llcheckboxctrl.h
+++ b/indra/llui/llcheckboxctrl.h
@@ -103,8 +103,6 @@ public:
virtual void setControlName(const std::string& control_name, LLView* context);
- void onButtonPress(const LLSD& data);
-
virtual BOOL isDirty() const; // Returns TRUE if the user has modified this control.
virtual void resetDirty(); // Clear dirty state
diff --git a/indra/llui/llclipboard.cpp b/indra/llui/llclipboard.cpp
index 984c4ec5fb..1d18cb2bb0 100644..100755
--- a/indra/llui/llclipboard.cpp
+++ b/indra/llui/llclipboard.cpp
@@ -34,103 +34,114 @@
#include "llview.h"
#include "llwindow.h"
-// Global singleton
-LLClipboard gClipboard;
-
-
-LLClipboard::LLClipboard()
+LLClipboard::LLClipboard() :
+ mGeneration(0)
{
+ reset();
}
-
LLClipboard::~LLClipboard()
{
+ reset();
}
-
-void LLClipboard::copyFromSubstring(const LLWString &src, S32 pos, S32 len, const LLUUID& source_id )
+void LLClipboard::reset()
{
- mSourceID = source_id;
- mString = src.substr(pos, len);
- LLView::getWindow()->copyTextToClipboard( mString );
+ // Increment the clipboard count
+ mGeneration++;
+ // Clear the clipboard
+ mObjects.clear();
+ mCutMode = false;
+ mString = LLWString();
}
-void LLClipboard::copyFromString(const LLWString &src, const LLUUID& source_id )
+// Copy the input uuid to the LL clipboard
+bool LLClipboard::copyToClipboard(const LLUUID& src, const LLAssetType::EType type)
{
- mSourceID = source_id;
- mString = src;
- LLView::getWindow()->copyTextToClipboard( mString );
+ reset();
+ return addToClipboard(src, type);
}
-const LLWString& LLClipboard::getPasteWString( LLUUID* source_id )
+// Add the input uuid to the LL clipboard
+// Convert the uuid to string and concatenate that string to the system clipboard if legit
+bool LLClipboard::addToClipboard(const LLUUID& src, const LLAssetType::EType type)
{
- if( mSourceID.notNull() )
+ bool res = false;
+ if (src.notNull())
{
- LLWString temp_string;
- LLView::getWindow()->pasteTextFromClipboard(temp_string);
-
- if( temp_string != mString )
+ res = true;
+ if (LLAssetType::lookupIsAssetIDKnowable(type))
{
- mSourceID.setNull();
- mString = temp_string;
+ LLWString source = utf8str_to_wstring(src.asString());
+ res = addToClipboard(source, 0, source.size());
+ }
+ if (res)
+ {
+ mObjects.push_back(src);
+ mGeneration++;
}
}
- else
- {
- LLView::getWindow()->pasteTextFromClipboard(mString);
- }
+ return res;
+}
- if( source_id )
+bool LLClipboard::pasteFromClipboard(std::vector<LLUUID>& inv_objects) const
+{
+ bool res = false;
+ S32 count = mObjects.size();
+ inv_objects.reserve(inv_objects.size() + count);
+ if (count > 0)
{
- *source_id = mSourceID;
+ res = true;
+ inv_objects.clear();
+ for (S32 i = 0; i < count; i++)
+ {
+ inv_objects.push_back(mObjects[i]);
+ }
}
-
- return mString;
+ return res;
}
+// Returns true if the LL Clipboard has pasteable items in it
+bool LLClipboard::hasContents() const
+{
+ return (mObjects.size() > 0);
+}
-BOOL LLClipboard::canPasteString() const
+// Returns true if the input uuid is in the list of clipboard objects
+bool LLClipboard::isOnClipboard(const LLUUID& object) const
{
- return LLView::getWindow()->isClipboardTextAvailable();
+ std::vector<LLUUID>::const_iterator iter = std::find(mObjects.begin(), mObjects.end(), object);
+ return (iter != mObjects.end());
}
+// Copy the input string to the LL and the system clipboard
+bool LLClipboard::copyToClipboard(const LLWString &src, S32 pos, S32 len, bool use_primary)
+{
+ return addToClipboard(src, pos, len, use_primary);
+}
-void LLClipboard::copyFromPrimarySubstring(const LLWString &src, S32 pos, S32 len, const LLUUID& source_id )
+// Concatenate the input string to the LL and the system clipboard
+bool LLClipboard::addToClipboard(const LLWString &src, S32 pos, S32 len, bool use_primary)
{
- mSourceID = source_id;
mString = src.substr(pos, len);
- LLView::getWindow()->copyTextToPrimary( mString );
+ return (use_primary ? LLView::getWindow()->copyTextToPrimary(mString) : LLView::getWindow()->copyTextToClipboard(mString));
}
-
-const LLWString& LLClipboard::getPastePrimaryWString( LLUUID* source_id )
+// Copy the System clipboard to the output string.
+// Manage the LL Clipboard / System clipboard consistency
+bool LLClipboard::pasteFromClipboard(LLWString &dst, bool use_primary)
{
- if( mSourceID.notNull() )
+ bool res = (use_primary ? LLView::getWindow()->pasteTextFromPrimary(dst) : LLView::getWindow()->pasteTextFromClipboard(dst));
+ if (res)
{
- LLWString temp_string;
- LLView::getWindow()->pasteTextFromPrimary(temp_string);
-
- if( temp_string != mString )
- {
- mSourceID.setNull();
- mString = temp_string;
- }
+ mString = dst;
}
- else
- {
- LLView::getWindow()->pasteTextFromPrimary(mString);
- }
-
- if( source_id )
- {
- *source_id = mSourceID;
- }
-
- return mString;
+ return res;
}
-
-BOOL LLClipboard::canPastePrimaryString() const
+// Return true if there's something on the System clipboard
+bool LLClipboard::isTextAvailable(bool use_primary) const
{
- return LLView::getWindow()->isPrimaryTextAvailable();
+ return (use_primary ? LLView::getWindow()->isPrimaryTextAvailable() : LLView::getWindow()->isClipboardTextAvailable());
}
+
diff --git a/indra/llui/llclipboard.h b/indra/llui/llclipboard.h
index 24cb46c3f4..58d80e2687 100644..100755
--- a/indra/llui/llclipboard.h
+++ b/indra/llui/llclipboard.h
@@ -27,39 +27,67 @@
#ifndef LL_LLCLIPBOARD_H
#define LL_LLCLIPBOARD_H
+#include <boost/function.hpp>
#include "llstring.h"
#include "lluuid.h"
+#include "llsingleton.h"
+#include "llassettype.h"
+#include "llinventory.h"
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLClipboard
+//
+// This class is used to cut/copy/paste text strings and inventory items around
+// the world. Use LLClipboard::instance().method() to use its methods.
+// Note that the text and UUIDs are loosely coupled only. There are few cases
+// where the viewer does offer a serialized version of the UUID on the clipboard.
+// In those case, the text is overridden when copying/cutting the item.
+// In all other cases, the text and the UUIDs are very much independent.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-class LLClipboard
+class LLClipboard : public LLSingleton<LLClipboard>
{
public:
LLClipboard();
~LLClipboard();
+
+ // Clears the clipboard
+ void reset();
+ // Returns the state of the clipboard so client can know if it has been modified (comparing with tracked state)
+ int getGeneration() const { return mGeneration; }
- /* We support two flavors of clipboard. The default is the explicitly
- copy-and-pasted clipboard. The second is the so-called 'primary' clipboard
- which is implicitly copied upon selection on platforms which expect this
- (i.e. X11/Linux). */
+ // Text strings management:
+ // ------------------------
+ // We support two flavors of text clipboards. The default is the explicitly
+ // copy-and-pasted clipboard. The second is the so-called 'primary' clipboard
+ // which is implicitly copied upon selection on platforms which expect this
+ // (i.e. X11/Linux, Mac).
+ bool copyToClipboard(const LLWString& src, S32 pos, S32 len, bool use_primary = false);
+ bool addToClipboard(const LLWString& src, S32 pos, S32 len, bool use_primary = false);
+ bool pasteFromClipboard(LLWString& dst, bool use_primary = false);
+ bool isTextAvailable(bool use_primary = false) const;
+
+ // Object list management:
+ // -----------------------
+ // Clears and adds one single object to the clipboard
+ bool copyToClipboard(const LLUUID& src, const LLAssetType::EType type = LLAssetType::AT_NONE);
+ // Adds one object to the current list of objects on the clipboard
+ bool addToClipboard(const LLUUID& src, const LLAssetType::EType type = LLAssetType::AT_NONE);
+ // Gets a copy of the objects on the clipboard
+ bool pasteFromClipboard(std::vector<LLUUID>& inventory_objects) const;
+
+ bool hasContents() const; // True if the clipboard has pasteable objects
+ bool isOnClipboard(const LLUUID& object) const; // True if the input object uuid is on the clipboard
- 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);
-
- void copyFromPrimarySubstring(const LLWString &copy_from, S32 pos, S32 len, const LLUUID& source_id = LLUUID::null );
- BOOL canPastePrimaryString() const;
- const LLWString& getPastePrimaryWString(LLUUID* source_id = NULL);
+ bool isCutMode() const { return mCutMode; }
+ void setCutMode(bool mode) { mCutMode = mode; mGeneration++; }
private:
- LLUUID mSourceID;
- LLWString mString;
+ std::vector<LLUUID> mObjects; // Objects on the clipboard. Can be empty while mString contains something licit (e.g. text from chat)
+ LLWString mString; // The text string. If mObjects is not empty, this string is reflecting them (UUIDs for the moment) if the asset type is knowable.
+ bool mCutMode; // This is a convenience flag for the viewer.
+ int mGeneration; // Incremented when the clipboard changes so that interested parties can check for changes on the clipboard.
};
-
-// Global singleton
-extern LLClipboard gClipboard;
-
-
#endif // LL_LLCLIPBOARD_H
diff --git a/indra/llui/llcombobox.cpp b/indra/llui/llcombobox.cpp
index 8b6a73af56..b32aea5ffa 100644..100755
--- a/indra/llui/llcombobox.cpp
+++ b/indra/llui/llcombobox.cpp
@@ -231,6 +231,10 @@ void LLComboBox::resetDirty()
}
}
+bool LLComboBox::itemExists(const std::string& name)
+{
+ return mList->getItemByLabel(name);
+}
// add item "name" to menu
LLScrollListItem* LLComboBox::add(const std::string& name, EAddPosition pos, BOOL enabled)
@@ -316,7 +320,7 @@ void LLComboBox::setValue(const LLSD& value)
LLScrollListItem* item = mList->getFirstSelected();
if (item)
{
- setLabel(getSelectedItemLabel());
+ updateLabel();
}
mLastSelectedIndex = mList->getFirstSelectedIndex();
}
@@ -384,6 +388,23 @@ void LLComboBox::setLabel(const LLStringExplicit& name)
}
}
+void LLComboBox::updateLabel()
+{
+ // Update the combo editor with the selected
+ // item label.
+ if (mTextEntry)
+ {
+ mTextEntry->setText(getSelectedItemLabel());
+ mTextEntry->setTentative(FALSE);
+ }
+
+ // If combo box doesn't allow text entry update
+ // the combo button label.
+ if (!mAllowTextEntry)
+ {
+ mButton->setLabel(getSelectedItemLabel());
+ }
+}
BOOL LLComboBox::remove(const std::string& name)
{
@@ -504,18 +525,22 @@ void LLComboBox::createLineEditor(const LLComboBox::Params& p)
else
{
mButton->setRect(rect);
- mButton->setTabStop(TRUE);
- mButton->setHAlign(LLFontGL::LEFT);
mButton->setLabel(mLabel.getString());
if (mTextEntry)
{
mTextEntry->setVisible(FALSE);
}
- mButton->setFollowsAll();
}
}
+void LLComboBox::setLeftTextPadding(S32 pad)
+{
+ S32 left_pad, right_pad;
+ mTextEntry->getTextPadding(&left_pad, &right_pad);
+ mTextEntry->setTextPadding(pad, right_pad);
+}
+
void* LLComboBox::getCurrentUserdata()
{
LLScrollListItem* item = mList->getFirstSelected();
@@ -533,7 +558,7 @@ void LLComboBox::showList()
LLCoordWindow window_size;
getWindow()->getSize(&window_size);
//HACK: shouldn't have to know about scale here
- mList->fitContents( 192, llfloor((F32)window_size.mY / LLUI::sGLScaleFactor.mV[VY]) - 50 );
+ mList->fitContents( 192, llfloor((F32)window_size.mY / LLUI::getScaleFactor().mV[VY]) - 50 );
// Make sure that we can see the whole list
LLRect root_view_local;
@@ -545,8 +570,7 @@ void LLComboBox::showList()
S32 min_width = getRect().getWidth();
S32 max_width = llmax(min_width, MAX_COMBO_WIDTH);
// make sure we have up to date content width metrics
- mList->calcColumnWidths();
- S32 list_width = llclamp(mList->getMaxContentWidth(), min_width, max_width);
+ S32 list_width = llclamp(mList->calcMaxContentWidth(), min_width, max_width);
if (mListPosition == BELOW)
{
@@ -595,7 +619,7 @@ void LLComboBox::showList()
}
mList->setOrigin(rect.mLeft, rect.mBottom);
mList->reshape(rect.getWidth(), rect.getHeight());
- mList->translateIntoRect(root_view_local, FALSE);
+ mList->translateIntoRect(root_view_local);
// Make sure we didn't go off bottom of screen
S32 x, y;
@@ -701,13 +725,13 @@ void LLComboBox::onItemSelected(const LLSD& data)
mLastSelectedIndex = getCurrentIndex();
if (mLastSelectedIndex != -1)
{
- setLabel(getSelectedItemLabel());
+ updateLabel();
if (mAllowTextEntry)
- {
- gFocusMgr.setKeyboardFocus(mTextEntry);
- mTextEntry->selectAll();
- }
+ {
+ gFocusMgr.setKeyboardFocus(mTextEntry);
+ mTextEntry->selectAll();
+ }
}
// hiding the list reasserts the old value stored in the text editor/dropdown button
hideList();
@@ -764,14 +788,21 @@ BOOL LLComboBox::handleKeyHere(KEY key, MASK mask)
// since the dropdown button eats the key
if (key == KEY_RETURN)
{
+ if (mask == MASK_NONE)
+ {
+ mOnReturnSignal(this, getValue());
+ }
+
// don't show list and don't eat key input when committing
// free-form text entry with RETURN since user already knows
// what they are trying to select
return FALSE;
}
// if selection has changed, pop open list
- else if (mList->getLastSelectedItem() != last_selected_item ||
- (key == KEY_DOWN || key == KEY_UP) && !mList->isEmpty())
+ else if (mList->getLastSelectedItem() != last_selected_item
+ || ((key == KEY_DOWN || key == KEY_UP)
+ && mList->getCanSelect()
+ && !mList->isEmpty()))
{
showList();
}
diff --git a/indra/llui/llcombobox.h b/indra/llui/llcombobox.h
index 74d64269bd..c9b1212b70 100644..100755
--- a/indra/llui/llcombobox.h
+++ b/indra/llui/llcombobox.h
@@ -134,6 +134,7 @@ public:
LLScrollListItem* addSeparator(EAddPosition pos = ADD_BOTTOM);
BOOL remove( S32 index ); // remove item by index, return TRUE if found and removed
void removeall() { clearRows(); }
+ bool itemExists(const std::string& name);
void sortByName(BOOL ascending = TRUE); // Sort the entries in the combobox by name
@@ -148,6 +149,9 @@ public:
// This is probably a UI abuse.
void setLabel(const LLStringExplicit& name);
+ // Updates the combobox label to match the selected list item.
+ void updateLabel();
+
BOOL remove(const std::string& name); // remove item "name", return TRUE if found and removed
BOOL setCurrentByIndex( S32 index );
@@ -187,12 +191,19 @@ public:
//========================================================================
+ void setLeftTextPadding(S32 pad);
+
void* getCurrentUserdata();
void setPrearrangeCallback( commit_callback_t cb ) { mPrearrangeCallback = cb; }
void setTextEntryCallback( commit_callback_t cb ) { mTextEntryCallback = cb; }
void setTextChangedCallback( commit_callback_t cb ) { mTextChangedCallback = cb; }
+ /**
+ * Connects callback to signal called when Return key is pressed.
+ */
+ boost::signals2::connection setReturnCallback( const commit_signal_t::slot_type& cb ) { return mOnReturnSignal.connect(cb); }
+
void setButtonVisible(BOOL visible);
void onButtonMouseDown();
@@ -225,6 +236,7 @@ private:
commit_callback_t mTextChangedCallback;
commit_callback_t mSelectionCallback;
boost::signals2::connection mTopLostSignalConnection;
+ commit_signal_t mOnReturnSignal;
S32 mLastSelectedIndex;
};
diff --git a/indra/llui/llcommandmanager.cpp b/indra/llui/llcommandmanager.cpp
new file mode 100755
index 0000000000..74ef8dd0c3
--- /dev/null
+++ b/indra/llui/llcommandmanager.cpp
@@ -0,0 +1,174 @@
+/**
+ * @file llcommandmanager.cpp
+ * @brief LLCommandManager class
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2011, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+// A control that displays the name of the chosen item, which when
+// clicked shows a scrolling box of options.
+
+#include "linden_common.h"
+
+#include "llcommandmanager.h"
+#include "lldir.h"
+#include "llerror.h"
+#include "llxuiparser.h"
+
+#include <boost/foreach.hpp>
+
+
+//
+// LLCommandId class
+//
+
+const LLCommandId LLCommandId::null = LLCommandId("null command");
+
+//
+// LLCommand class
+//
+
+LLCommand::Params::Params()
+ : available_in_toybox("available_in_toybox", false)
+ , icon("icon")
+ , label_ref("label_ref")
+ , name("name")
+ , tooltip_ref("tooltip_ref")
+ , execute_function("execute_function")
+ , execute_parameters("execute_parameters")
+ , execute_stop_function("execute_stop_function")
+ , execute_stop_parameters("execute_stop_parameters")
+ , is_enabled_function("is_enabled_function")
+ , is_enabled_parameters("is_enabled_parameters")
+ , is_running_function("is_running_function")
+ , is_running_parameters("is_running_parameters")
+ , is_starting_function("is_starting_function")
+ , is_starting_parameters("is_starting_parameters")
+ , is_flashing_allowed("is_flashing_allowed", false)
+{
+}
+
+LLCommand::LLCommand(const LLCommand::Params& p)
+ : mIdentifier(p.name)
+ , mAvailableInToybox(p.available_in_toybox)
+ , mIcon(p.icon)
+ , mLabelRef(p.label_ref)
+ , mName(p.name)
+ , mTooltipRef(p.tooltip_ref)
+ , mExecuteFunction(p.execute_function)
+ , mExecuteParameters(p.execute_parameters)
+ , mExecuteStopFunction(p.execute_stop_function)
+ , mExecuteStopParameters(p.execute_stop_parameters)
+ , mIsEnabledFunction(p.is_enabled_function)
+ , mIsEnabledParameters(p.is_enabled_parameters)
+ , mIsRunningFunction(p.is_running_function)
+ , mIsRunningParameters(p.is_running_parameters)
+ , mIsStartingFunction(p.is_starting_function)
+ , mIsStartingParameters(p.is_starting_parameters)
+ , mIsFlashingAllowed(p.is_flashing_allowed)
+{
+}
+
+
+//
+// LLCommandManager class
+//
+
+LLCommandManager::LLCommandManager()
+{
+}
+
+LLCommandManager::~LLCommandManager()
+{
+ for (CommandVector::iterator cmdIt = mCommands.begin(); cmdIt != mCommands.end(); ++cmdIt)
+ {
+ LLCommand * command = *cmdIt;
+
+ delete command;
+ }
+}
+
+U32 LLCommandManager::commandCount() const
+{
+ return mCommands.size();
+}
+
+LLCommand * LLCommandManager::getCommand(U32 commandIndex)
+{
+ return mCommands[commandIndex];
+}
+
+LLCommand * LLCommandManager::getCommand(const LLCommandId& commandId)
+{
+ LLCommand * command_match = NULL;
+
+ CommandIndexMap::const_iterator found = mCommandIndices.find(commandId.uuid());
+
+ if (found != mCommandIndices.end())
+ {
+ command_match = mCommands[found->second];
+ }
+
+ return command_match;
+}
+
+void LLCommandManager::addCommand(LLCommand * command)
+{
+ LLCommandId command_id = command->id();
+ mCommandIndices[command_id.uuid()] = mCommands.size();
+ mCommands.push_back(command);
+
+ LL_DEBUGS() << "Successfully added command: " << command->name() << LL_ENDL;
+}
+
+//static
+bool LLCommandManager::load()
+{
+ LLCommandManager& mgr = LLCommandManager::instance();
+
+ std::string commands_file = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "commands.xml");
+
+ LLCommandManager::Params commandsParams;
+
+ LLSimpleXUIParser parser;
+
+ if (!parser.readXUI(commands_file, commandsParams))
+ {
+ LL_ERRS() << "Unable to load xml file: " << commands_file << LL_ENDL;
+ return false;
+ }
+
+ if (!commandsParams.validateBlock())
+ {
+ LL_ERRS() << "Invalid commands file: " << commands_file << LL_ENDL;
+ return false;
+ }
+
+ BOOST_FOREACH(LLCommand::Params& commandParams, commandsParams.commands)
+ {
+ LLCommand * command = new LLCommand(commandParams);
+
+ mgr.addCommand(command);
+ }
+
+ return true;
+}
diff --git a/indra/llui/llcommandmanager.h b/indra/llui/llcommandmanager.h
new file mode 100755
index 0000000000..ff5a8a3257
--- /dev/null
+++ b/indra/llui/llcommandmanager.h
@@ -0,0 +1,208 @@
+/**
+ * @file llcommandmanager.h
+ * @brief LLCommandManager class to hold commands
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2011, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLCOMMANDMANAGER_H
+#define LL_LLCOMMANDMANAGER_H
+
+#include "llinitparam.h"
+#include "llsingleton.h"
+
+
+class LLCommand;
+class LLCommandManager;
+
+
+class LLCommandId
+{
+public:
+ friend class LLCommand;
+ friend class LLCommandManager;
+
+ struct Params : public LLInitParam::Block<Params>
+ {
+ Mandatory<std::string> name;
+
+ Params()
+ : name("name")
+ {}
+ };
+
+ LLCommandId(const std::string& name)
+ {
+ mUUID = LLUUID::generateNewID(name);
+ }
+
+ LLCommandId(const Params& p)
+ {
+ mUUID = LLUUID::generateNewID(p.name);
+ }
+
+ LLCommandId(const LLUUID& uuid)
+ : mUUID(uuid)
+ {}
+
+ const LLUUID& uuid() const { return mUUID; }
+
+ bool operator!=(const LLCommandId& command) const
+ {
+ return (mUUID != command.mUUID);
+ }
+
+ bool operator==(const LLCommandId& command) const
+ {
+ return (mUUID == command.mUUID);
+ }
+
+ static const LLCommandId null;
+
+private:
+ LLUUID mUUID;
+};
+
+typedef std::list<LLCommandId> command_id_list_t;
+
+
+class LLCommand
+{
+public:
+ struct Params : public LLInitParam::Block<Params>
+ {
+ Mandatory<bool> available_in_toybox;
+ Mandatory<std::string> icon;
+ Mandatory<std::string> label_ref;
+ Mandatory<std::string> name;
+ Mandatory<std::string> tooltip_ref;
+
+ Mandatory<std::string> execute_function;
+ Optional<LLSD> execute_parameters;
+
+ Optional<std::string> execute_stop_function;
+ Optional<LLSD> execute_stop_parameters;
+
+ Optional<std::string> is_enabled_function;
+ Optional<LLSD> is_enabled_parameters;
+
+ Optional<std::string> is_running_function;
+ Optional<LLSD> is_running_parameters;
+
+ Optional<std::string> is_starting_function;
+ Optional<LLSD> is_starting_parameters;
+
+ Optional<bool> is_flashing_allowed;
+
+ Params();
+ };
+
+ LLCommand(const LLCommand::Params& p);
+
+ const bool availableInToybox() const { return mAvailableInToybox; }
+ const std::string& icon() const { return mIcon; }
+ const LLCommandId& id() const { return mIdentifier; }
+ const std::string& labelRef() const { return mLabelRef; }
+ const std::string& name() const { return mName; }
+ const std::string& tooltipRef() const { return mTooltipRef; }
+
+ const std::string& executeFunctionName() const { return mExecuteFunction; }
+ const LLSD& executeParameters() const { return mExecuteParameters; }
+
+ const std::string& executeStopFunctionName() const { return mExecuteStopFunction; }
+ const LLSD& executeStopParameters() const { return mExecuteStopParameters; }
+
+ const std::string& isEnabledFunctionName() const { return mIsEnabledFunction; }
+ const LLSD& isEnabledParameters() const { return mIsEnabledParameters; }
+
+ const std::string& isRunningFunctionName() const { return mIsRunningFunction; }
+ const LLSD& isRunningParameters() const { return mIsRunningParameters; }
+
+ const std::string& isStartingFunctionName() const { return mIsStartingFunction; }
+ const LLSD& isStartingParameters() const { return mIsStartingParameters; }
+
+ bool isFlashingAllowed() const { return mIsFlashingAllowed; }
+
+private:
+ LLCommandId mIdentifier;
+
+ bool mAvailableInToybox;
+ std::string mIcon;
+ std::string mLabelRef;
+ std::string mName;
+ std::string mTooltipRef;
+
+ std::string mExecuteFunction;
+ LLSD mExecuteParameters;
+
+ std::string mExecuteStopFunction;
+ LLSD mExecuteStopParameters;
+
+ std::string mIsEnabledFunction;
+ LLSD mIsEnabledParameters;
+
+ std::string mIsRunningFunction;
+ LLSD mIsRunningParameters;
+
+ std::string mIsStartingFunction;
+ LLSD mIsStartingParameters;
+
+ bool mIsFlashingAllowed;
+};
+
+
+class LLCommandManager
+: public LLSingleton<LLCommandManager>
+{
+public:
+ struct Params : public LLInitParam::Block<Params>
+ {
+ Multiple< LLCommand::Params, AtLeast<1> > commands;
+
+ Params()
+ : commands("command")
+ {
+ }
+ };
+
+ LLCommandManager();
+ ~LLCommandManager();
+
+ U32 commandCount() const;
+ LLCommand * getCommand(U32 commandIndex);
+ LLCommand * getCommand(const LLCommandId& commandId);
+
+ static bool load();
+
+protected:
+ void addCommand(LLCommand * command);
+
+private:
+ typedef std::map<LLUUID, U32> CommandIndexMap;
+ typedef std::vector<LLCommand *> CommandVector;
+
+ CommandVector mCommands;
+ CommandIndexMap mCommandIndices;
+};
+
+
+#endif // LL_LLCOMMANDMANAGER_H
diff --git a/indra/llui/llconsole.cpp b/indra/llui/llconsole.cpp
index 06bad1f371..26ae31cac6 100644..100755
--- a/indra/llui/llconsole.cpp
+++ b/indra/llui/llconsole.cpp
@@ -52,7 +52,6 @@ extern void AddNewDebugConsoleToLCD(const LLWString &newLine);
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");
@@ -132,6 +131,9 @@ void LLConsole::setFontSize(S32 size_index)
void LLConsole::draw()
{
+ // Units in pixels
+ static const F32 padding_horizontal = 10;
+ static const F32 padding_vertical = 3;
LLGLSUIDefault gls_ui;
// skip lines added more than mLinePersistTime ago
@@ -176,11 +178,9 @@ void LLConsole::draw()
// draw remaining lines
F32 y_pos = 0.f;
- LLUIImagePtr imagep = LLUI::getUIImage("Rounded_Square");
+ LLUIImagePtr imagep = LLUI::getUIImage("transparent");
-// 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 = LLUIColorTable::instance().getColor("ConsoleBackground");
LLColor4 color = LLUIColorTable::instance().getColor("ConsoleBackground");
color.mV[VALPHA] *= console_opacity;
@@ -188,8 +188,8 @@ void LLConsole::draw()
for(paragraph_it = mParagraphs.rbegin(); paragraph_it != mParagraphs.rend(); paragraph_it++)
{
- S32 target_height = llfloor( (*paragraph_it).mLines.size() * line_height + 8);
- S32 target_width = llfloor( (*paragraph_it).mMaxWidth +15);
+ S32 target_height = llfloor( (*paragraph_it).mLines.size() * line_height + padding_vertical);
+ S32 target_width = llfloor( (*paragraph_it).mMaxWidth + padding_horizontal);
y_pos += ((*paragraph_it).mLines.size()) * line_height;
imagep->drawSolid(-14, (S32)(y_pos + line_height - target_height), target_width, target_height, color);
@@ -234,7 +234,7 @@ void LLConsole::draw()
y_off += line_height;
}
}
- y_pos += 8;
+ y_pos += padding_vertical;
}
}
@@ -242,8 +242,6 @@ void LLConsole::draw()
void LLConsole::Paragraph::makeParagraphColorSegments (const LLColor4 &color)
{
LLSD paragraph_color_segments;
- LLColor4 lcolor=color;
-
paragraph_color_segments[0]["text"] =wstring_to_utf8str(mParagraphText);
LLSD color_sd = color.getValue();
paragraph_color_segments[0]["color"]=color_sd;
@@ -371,9 +369,7 @@ LLConsole::Paragraph::Paragraph (LLWString str, const LLColor4 &color, F32 add_t
// static
void LLConsole::updateClass()
{
- LLInstanceTrackerScopedGuard guard;
-
- for (instance_iter it = guard.beginInstances(); it != guard.endInstances(); ++it)
+ for (instance_iter it = beginInstances(); it != endInstances(); ++it)
{
it->update();
}
diff --git a/indra/llui/llconsole.h b/indra/llui/llconsole.h
index bb8ea50bed..5ff05698b0 100644..100755
--- a/indra/llui/llconsole.h
+++ b/indra/llui/llconsole.h
@@ -37,6 +37,7 @@ class LLSD;
class LLConsole : public LLFixedBuffer, public LLUICtrl, public LLInstanceTracker<LLConsole>
{
public:
+
typedef enum e_font_size
{
MONOSPACE = -1,
@@ -54,7 +55,7 @@ public:
persist_time("persist_time", 0.f), // forever
font_size_index("font_size_index")
{
- mouse_opaque(false);
+ changeDefault(mouse_opaque, false);
}
};
protected:
diff --git a/indra/llui/llcontainerview.cpp b/indra/llui/llcontainerview.cpp
index e01e331acf..727fbe850e 100644..100755
--- a/indra/llui/llcontainerview.cpp
+++ b/indra/llui/llcontainerview.cpp
@@ -49,7 +49,6 @@ LLContainerView::LLContainerView(const LLContainerView::Params& p)
mLabel(p.label),
mDisplayChildren(p.display_children)
{
- mCollapsible = TRUE;
mScrollContainer = NULL;
}
@@ -75,6 +74,11 @@ bool LLContainerView::addChild(LLView* child, S32 tab_group)
return res;
}
+BOOL LLContainerView::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ return handleMouseDown(x, y, mask);
+}
+
BOOL LLContainerView::handleMouseDown(S32 x, S32 y, MASK mask)
{
BOOL handled = FALSE;
@@ -84,7 +88,7 @@ BOOL LLContainerView::handleMouseDown(S32 x, S32 y, MASK mask)
}
if (!handled)
{
- if( mCollapsible && mShowLabel && (y >= getRect().getHeight() - 10) )
+ if( mShowLabel && (y >= getRect().getHeight() - 10) )
{
setDisplayChildren(!mDisplayChildren);
reshape(getRect().getWidth(), getRect().getHeight(), FALSE);
@@ -163,7 +167,7 @@ void LLContainerView::arrange(S32 width, S32 height, BOOL called_from_parent)
//LLView *childp;
// These will be used for the children
- left = 4;
+ left = 10;
top = getRect().getHeight() - 4;
right = width - 2;
bottom = top;
@@ -184,7 +188,7 @@ void LLContainerView::arrange(S32 width, S32 height, BOOL called_from_parent)
LLView *childp = *child_iter;
if (!childp->getVisible())
{
- llwarns << "Incorrect visibility!" << llendl;
+ LL_WARNS() << "Incorrect visibility!" << LL_ENDL;
}
LLRect child_rect = childp->getRequiredRect();
child_height += child_rect.getHeight();
@@ -196,24 +200,24 @@ void LLContainerView::arrange(S32 width, S32 height, BOOL called_from_parent)
if (total_height < height)
total_height = height;
+ LLRect my_rect = getRect();
if (followsTop())
{
- // HACK: casting away const. Should use setRect or some helper function instead.
- const_cast<LLRect&>(getRect()).mBottom = getRect().mTop - total_height;
+ my_rect.mBottom = my_rect.mTop - total_height;
}
else
{
- // HACK: casting away const. Should use setRect or some helper function instead.
- const_cast<LLRect&>(getRect()).mTop = getRect().mBottom + total_height;
+ my_rect.mTop = my_rect.mBottom + total_height;
}
- // HACK: casting away const. Should use setRect or some helper function instead.
- const_cast<LLRect&>(getRect()).mRight = getRect().mLeft + width;
+
+ my_rect.mRight = my_rect.mLeft + width;
+ setRect(my_rect);
top = total_height;
if (mShowLabel)
- {
- top -= 20;
- }
+ {
+ top -= 20;
+ }
bottom = top;
diff --git a/indra/llui/llcontainerview.h b/indra/llui/llcontainerview.h
index 7d3d5cf787..ac92b19977 100644..100755
--- a/indra/llui/llcontainerview.h
+++ b/indra/llui/llcontainerview.h
@@ -50,7 +50,7 @@ public:
show_label("show_label", FALSE),
display_children("display_children", TRUE)
{
- mouse_opaque(false);
+ changeDefault(mouse_opaque, false);
}
};
@@ -66,6 +66,7 @@ public:
/*virtual*/ BOOL postBuild();
/*virtual*/ bool addChild(LLView* view, S32 tab_group = 0);
+ /*virtual*/ BOOL handleDoubleClick(S32 x, S32 y, MASK mask);
/*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask);
/*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask);
@@ -87,8 +88,5 @@ public:
protected:
BOOL mDisplayChildren;
std::string mLabel;
-public:
- BOOL mCollapsible;
-
};
#endif // LL_CONTAINERVIEW_
diff --git a/indra/llui/llctrlselectioninterface.cpp b/indra/llui/llctrlselectioninterface.cpp
index 7e886aff48..7e886aff48 100644..100755
--- a/indra/llui/llctrlselectioninterface.cpp
+++ b/indra/llui/llctrlselectioninterface.cpp
diff --git a/indra/llui/llctrlselectioninterface.h b/indra/llui/llctrlselectioninterface.h
index 2cdcbd88fe..a7b089c8f9 100644..100755
--- a/indra/llui/llctrlselectioninterface.h
+++ b/indra/llui/llctrlselectioninterface.h
@@ -28,8 +28,8 @@
#define LLCTRLSELECTIONINTERFACE_H
#include "stdtypes.h"
-#include "stdenums.h"
#include "llstring.h"
+#include "llui.h"
class LLSD;
class LLUUID;
diff --git a/indra/llui/lldockablefloater.cpp b/indra/llui/lldockablefloater.cpp
index ca2dc644a4..3396213f1c 100644..100755
--- a/indra/llui/lldockablefloater.cpp
+++ b/indra/llui/lldockablefloater.cpp
@@ -82,7 +82,7 @@ BOOL LLDockableFloater::postBuild()
mForceDocking = true;
}
- mDockTongue = LLUI::getUIImage("windows/Flyout_Pointer.png");
+ mDockTongue = LLUI::getUIImage("Flyout_Pointer");
LLFloater::setDocked(true);
return LLView::postBuild();
}
@@ -162,10 +162,15 @@ void LLDockableFloater::setVisible(BOOL visible)
void LLDockableFloater::setMinimized(BOOL minimize)
{
- if(minimize)
+ if(minimize && isDocked())
{
+ // minimizing a docked floater just hides it
setVisible(FALSE);
}
+ else
+ {
+ LLFloater::setMinimized(minimize);
+ }
}
LLView * LLDockableFloater::getDockWidget()
@@ -234,8 +239,21 @@ void LLDockableFloater::setDockControl(LLDockControl* dockControl)
setDocked(isDocked());
}
-const LLUIImagePtr& LLDockableFloater::getDockTongue()
+const LLUIImagePtr& LLDockableFloater::getDockTongue(LLDockControl::DocAt dock_side)
{
+ switch(dock_side)
+ {
+ case LLDockControl::LEFT:
+ mDockTongue = LLUI::getUIImage("Flyout_Left");
+ break;
+ case LLDockControl::RIGHT:
+ mDockTongue = LLUI::getUIImage("Flyout_Right");
+ break;
+ default:
+ mDockTongue = LLUI::getUIImage("Flyout_Pointer");
+ break;
+ }
+
return mDockTongue;
}
diff --git a/indra/llui/lldockablefloater.h b/indra/llui/lldockablefloater.h
index 8deb6c1159..89c9852f4a 100644..100755
--- a/indra/llui/lldockablefloater.h
+++ b/indra/llui/lldockablefloater.h
@@ -113,6 +113,8 @@ public:
bool getUniqueDocking() { return mUniqueDocking; }
bool getUseTongue() { return mUseTongue; }
+
+ void setUseTongue(bool use_tongue) { mUseTongue = use_tongue;}
private:
/**
* Provides unique of dockable floater.
@@ -122,7 +124,7 @@ private:
protected:
void setDockControl(LLDockControl* dockControl);
- const LLUIImagePtr& getDockTongue();
+ const LLUIImagePtr& getDockTongue(LLDockControl::DocAt dock_side = LLDockControl::TOP);
// Checks if docking should be forced.
// It may be useful e.g. if floater created in mouselook mode (see EXT-5609)
diff --git a/indra/llui/lldockcontrol.cpp b/indra/llui/lldockcontrol.cpp
index f6f5a0beb3..bd42497cb6 100644..100755
--- a/indra/llui/lldockcontrol.cpp
+++ b/indra/llui/lldockcontrol.cpp
@@ -31,7 +31,6 @@
LLDockControl::LLDockControl(LLView* dockWidget, LLFloater* dockableFloater,
const LLUIImagePtr& dockTongue, DocAt dockAt, get_allowed_rect_callback_t get_allowed_rect_callback) :
- mDockWidget(dockWidget),
mDockableFloater(dockableFloater),
mDockTongue(dockTongue),
mDockTongueX(0),
@@ -39,6 +38,11 @@ LLDockControl::LLDockControl(LLView* dockWidget, LLFloater* dockableFloater,
{
mDockAt = dockAt;
+ if (dockWidget != NULL)
+ {
+ mDockWidgetHandle = dockWidget->getHandle();
+ }
+
if (dockableFloater->isDocked())
{
on();
@@ -62,7 +66,7 @@ LLDockControl::LLDockControl(LLView* dockWidget, LLFloater* dockableFloater,
repositionDockable();
}
- if (mDockWidget != NULL)
+ if (getDock() != NULL)
{
mDockWidgetVisible = isDockVisible();
}
@@ -78,33 +82,39 @@ LLDockControl::~LLDockControl()
void LLDockControl::setDock(LLView* dockWidget)
{
- mDockWidget = dockWidget;
- if (mDockWidget != NULL)
+ if (dockWidget != NULL)
{
+ mDockWidgetHandle = dockWidget->getHandle();
repositionDockable();
mDockWidgetVisible = isDockVisible();
}
else
{
+ mDockWidgetHandle = LLHandle<LLView>();
mDockWidgetVisible = false;
}
}
void LLDockControl::getAllowedRect(LLRect& rect)
{
- rect = mDockableFloater->getRootView()->getRect();
+ rect = mDockableFloater->getRootView()->getChild<LLView>("non_toolbar_panel")->getRect();
}
void LLDockControl::repositionDockable()
{
- LLRect dockRect = mDockWidget->calcScreenRect();
+ if (!getDock()) return;
+ LLRect dockRect = getDock()->calcScreenRect();
LLRect rootRect;
+ LLRect floater_rect = mDockableFloater->calcScreenRect();
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)
+ // recalculate dockable position if:
+ if (mPrevDockRect != dockRect //dock position changed
+ || mDockWidgetVisible != isDockVisible() //dock visibility changed
+ || mRootRect != rootRect //root view rect changed
+ || mFloaterRect != floater_rect //floater rect changed
+ || mRecalculateDockablePosition //recalculation is forced
+ )
{
// undock dockable and off() if dock not visible
if (!isDockVisible())
@@ -135,7 +145,8 @@ void LLDockControl::repositionDockable()
mPrevDockRect = dockRect;
mRootRect = rootRect;
- mRecalculateDocablePosition = false;
+ mFloaterRect = floater_rect;
+ mRecalculateDockablePosition = false;
mDockWidgetVisible = isDockVisible();
}
}
@@ -144,13 +155,13 @@ bool LLDockControl::isDockVisible()
{
bool res = true;
- if (mDockWidget != NULL)
+ if (getDock() != NULL)
{
//we should check all hierarchy
- res = mDockWidget->isInVisibleChain();
+ res = getDock()->isInVisibleChain();
if (res)
{
- LLRect dockRect = mDockWidget->calcScreenRect();
+ LLRect dockRect = getDock()->calcScreenRect();
switch (mDockAt)
{
@@ -160,8 +171,10 @@ bool LLDockControl::isDockVisible()
case TOP:
{
// check is dock inside parent rect
+ // assume that parent for all dockable floaters
+ // is the root view
LLRect dockParentRect =
- mDockWidget->getParent()->calcScreenRect();
+ getDock()->getRootView()->calcScreenRect();
if (dockRect.mRight <= dockParentRect.mLeft
|| dockRect.mLeft >= dockParentRect.mRight)
{
@@ -181,7 +194,7 @@ bool LLDockControl::isDockVisible()
void LLDockControl::moveDockable()
{
// calculate new dockable position
- LLRect dockRect = mDockWidget->calcScreenRect();
+ LLRect dockRect = getDock()->calcScreenRect();
LLRect rootRect;
mGetAllowedRectCallback(rootRect);
@@ -200,21 +213,33 @@ void LLDockControl::moveDockable()
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 = dockRect.mLeft - dockableRect.getWidth();
+ y = dockRect.getCenterY() + dockableRect.getHeight() / 2;
+
+ if (use_tongue)
{
- x = rootRect.mLeft;
+ x -= mDockTongue->getWidth();
}
- if (x + dockableRect.getWidth() > rootRect.mRight)
+
+ mDockTongueX = dockableRect.mRight;
+ mDockTongueY = dockableRect.getCenterY() - mDockTongue->getHeight() / 2;
+
+ break;
+
+ case RIGHT:
+
+ x = dockRect.mRight;
+ y = dockRect.getCenterY() + dockableRect.getHeight() / 2;
+
+ if (use_tongue)
{
- x = rootRect.mRight - dockableRect.getWidth();
+ x += mDockTongue->getWidth();
}
-
- mDockTongueX = x + dockableRect.getWidth()/2 - mDockTongue->getWidth() / 2;
-
- mDockTongueY = dockRect.mTop;
+
+ mDockTongueX = dockRect.mRight;
+ mDockTongueY = dockableRect.getCenterY() - mDockTongue->getHeight() / 2;
+
break;
case TOP:
@@ -243,7 +268,7 @@ void LLDockControl::moveDockable()
// calculate dock tongue position
- dockParentRect = mDockWidget->getParent()->calcScreenRect();
+ dockParentRect = getDock()->getParent()->calcScreenRect();
if (dockRect.getCenterX() < dockParentRect.mLeft)
{
mDockTongueX = dockParentRect.mLeft - mDockTongue->getWidth() / 2;
@@ -279,7 +304,7 @@ void LLDockControl::moveDockable()
}
// calculate dock tongue position
- dockParentRect = mDockWidget->getParent()->calcScreenRect();
+ dockParentRect = getDock()->getParent()->calcScreenRect();
if (dockRect.getCenterX() < dockParentRect.mLeft)
{
mDockTongueX = dockParentRect.mLeft - mDockTongue->getWidth() / 2;
@@ -297,7 +322,7 @@ void LLDockControl::moveDockable()
break;
}
- S32 max_available_height = rootRect.getHeight() - mDockTongueY - mDockTongue->getHeight();
+ S32 max_available_height = rootRect.getHeight() - (rootRect.mBottom - mDockTongueY) - mDockTongue->getHeight();
// A floater should be shrunk so it doesn't cover a part of its docking tongue and
// there is a space between a dockable floater and a control to which it is docked.
@@ -312,13 +337,12 @@ void LLDockControl::moveDockable()
dockableRect.setLeftTopAndSize(x, y, dockableRect.getWidth(),
dockableRect.getHeight());
}
+
LLRect localDocableParentRect;
- mDockableFloater->getParent()->screenRectToLocal(dockableRect,
- &localDocableParentRect);
- mDockableFloater->setRect(localDocableParentRect);
- mDockableFloater->screenPointToLocal(mDockTongueX, mDockTongueY,
- &mDockTongueX, &mDockTongueY);
+ mDockableFloater->getParent()->screenRectToLocal(dockableRect, &localDocableParentRect);
+ mDockableFloater->setRect(localDocableParentRect);
+ mDockableFloater->screenPointToLocal(mDockTongueX, mDockTongueY, &mDockTongueX, &mDockTongueY);
}
@@ -327,7 +351,7 @@ void LLDockControl::on()
if (isDockVisible())
{
mEnabled = true;
- mRecalculateDocablePosition = true;
+ mRecalculateDockablePosition = true;
}
}
@@ -338,7 +362,7 @@ void LLDockControl::off()
void LLDockControl::forceRecalculatePosition()
{
- mRecalculateDocablePosition = true;
+ mRecalculateDockablePosition = true;
}
void LLDockControl::drawToungue()
diff --git a/indra/llui/lldockcontrol.h b/indra/llui/lldockcontrol.h
index 2e7359245f..98a9c7236d 100644..100755
--- a/indra/llui/lldockcontrol.h
+++ b/indra/llui/lldockcontrol.h
@@ -43,6 +43,7 @@ public:
{
TOP,
LEFT,
+ RIGHT,
BOTTOM
};
@@ -62,7 +63,7 @@ public:
void setDock(LLView* dockWidget);
LLView* getDock()
{
- return mDockWidget;
+ return mDockWidgetHandle.get();
}
void repositionDockable();
void drawToungue();
@@ -79,12 +80,13 @@ private:
private:
get_allowed_rect_callback_t mGetAllowedRectCallback;
bool mEnabled;
- bool mRecalculateDocablePosition;
+ bool mRecalculateDockablePosition;
bool mDockWidgetVisible;
DocAt mDockAt;
- LLView* mDockWidget;
+ LLHandle<LLView> mDockWidgetHandle;
LLRect mPrevDockRect;
LLRect mRootRect;
+ LLRect mFloaterRect;
LLFloater* mDockableFloater;
LLUIImagePtr mDockTongue;
S32 mDockTongueX;
diff --git a/indra/llui/lldraghandle.cpp b/indra/llui/lldraghandle.cpp
index 42e6c3c786..48bf5bb80f 100644..100755
--- a/indra/llui/lldraghandle.cpp
+++ b/indra/llui/lldraghandle.cpp
@@ -46,7 +46,6 @@ const S32 LEADING_PAD = 5;
const S32 TITLE_HPAD = 8;
const S32 BORDER_PAD = 1;
const S32 LEFT_PAD = BORDER_PAD + TITLE_HPAD + LEADING_PAD;
-const S32 RIGHT_PAD = BORDER_PAD + 32; // HACK: space for close btn and minimize btn
S32 LLDragHandle::sSnapMargin = 5;
@@ -244,7 +243,7 @@ void LLDragHandleTop::reshapeTitleBox()
const LLFontGL* font = LLFontGL::getFontSansSerif();
S32 title_width = getRect().getWidth();
title_width -= LEFT_PAD + 2 * BORDER_PAD + getButtonsRect().getWidth();
- S32 title_height = llround(font->getLineHeight());
+ S32 title_height = font->getLineHeight();
LLRect title_rect;
title_rect.setLeftTopAndSize(
LEFT_PAD,
@@ -315,14 +314,15 @@ BOOL LLDragHandle::handleHover(S32 x, S32 y, MASK mask)
S32 delta_y = screen_y - mDragLastScreenY;
// if dragging a docked floater we want to undock
- if (((LLFloater*)getParent())->isDocked())
+ LLFloater * parent = dynamic_cast<LLFloater *>(getParent());
+ if (parent && parent->isDocked())
{
const S32 SLOP = 12;
if (delta_y <= -SLOP ||
delta_y >= SLOP)
{
- ((LLFloater*)getParent())->setDocked(false, false);
+ parent->setDocked(false, false);
return TRUE;
}
else
@@ -365,13 +365,13 @@ BOOL LLDragHandle::handleHover(S32 x, S32 y, MASK mask)
mDragLastScreenY += delta_y;
getWindow()->setCursor(UI_CURSOR_ARROW);
- lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (active)" <<llendl;
+ LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (active)" <<LL_ENDL;
handled = TRUE;
}
else
{
getWindow()->setCursor(UI_CURSOR_ARROW);
- lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (inactive)" << llendl;
+ LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (inactive)" << LL_ENDL;
handled = TRUE;
}
diff --git a/indra/llui/lldraghandle.h b/indra/llui/lldraghandle.h
index 7c56475e75..e095e577b1 100644..100755
--- a/indra/llui/lldraghandle.h
+++ b/indra/llui/lldraghandle.h
@@ -51,8 +51,8 @@ public:
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);
+ changeDefault(mouse_opaque, true);
+ changeDefault(follows.flags, FOLLOWS_ALL);
}
};
void initFromParams(const Params&);
diff --git a/indra/llui/lleditmenuhandler.cpp b/indra/llui/lleditmenuhandler.cpp
index d48237e377..d48237e377 100644..100755
--- a/indra/llui/lleditmenuhandler.cpp
+++ b/indra/llui/lleditmenuhandler.cpp
diff --git a/indra/llui/lleditmenuhandler.h b/indra/llui/lleditmenuhandler.h
index 0932f094ef..0932f094ef 100644..100755
--- a/indra/llui/lleditmenuhandler.h
+++ b/indra/llui/lleditmenuhandler.h
diff --git a/indra/llui/llf32uictrl.cpp b/indra/llui/llf32uictrl.cpp
index d186f085b4..d186f085b4 100644..100755
--- a/indra/llui/llf32uictrl.cpp
+++ b/indra/llui/llf32uictrl.cpp
diff --git a/indra/llui/llf32uictrl.h b/indra/llui/llf32uictrl.h
index 6e648f9102..6e648f9102 100644..100755
--- a/indra/llui/llf32uictrl.h
+++ b/indra/llui/llf32uictrl.h
diff --git a/indra/llui/llfiltereditor.cpp b/indra/llui/llfiltereditor.cpp
index d62874d793..d62874d793 100644..100755
--- a/indra/llui/llfiltereditor.cpp
+++ b/indra/llui/llfiltereditor.cpp
diff --git a/indra/llui/llfiltereditor.h b/indra/llui/llfiltereditor.h
index 710699fdc1..3a05bc05a1 100644..100755
--- a/indra/llui/llfiltereditor.h
+++ b/indra/llui/llfiltereditor.h
@@ -42,12 +42,7 @@ class LLFilterEditor : public LLSearchEditor
{
public:
struct Params : public LLInitParam::Block<Params, LLSearchEditor::Params>
- {
- Params()
- {
- name = "filter_editor";
- }
- };
+ {};
protected:
LLFilterEditor(const Params&);
diff --git a/indra/llui/llflashtimer.cpp b/indra/llui/llflashtimer.cpp
new file mode 100755
index 0000000000..6d9c429b08
--- /dev/null
+++ b/indra/llui/llflashtimer.cpp
@@ -0,0 +1,98 @@
+/**
+ * @file llflashtimer.cpp
+ * @brief LLFlashTimer class implementation
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2012, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+#include "llflashtimer.h"
+#include "lleventtimer.h"
+#include "llui.h"
+
+LLFlashTimer::LLFlashTimer(callback_t cb, S32 count, F32 period)
+: LLEventTimer(period),
+ mCallback(cb),
+ mCurrentTickCount(0),
+ mIsFlashingInProgress(false),
+ mIsCurrentlyHighlighted(false),
+ mUnset(false)
+{
+ mEventTimer.stop();
+
+ // By default use settings from settings.xml to be able change them via Debug settings. See EXT-5973.
+ // Due to Timer is implemented as derived class from EventTimer it is impossible to change period
+ // in runtime. So, both settings are made as required restart.
+ mFlashCount = 2 * ((count > 0) ? count : LLUI::sSettingGroups["config"]->getS32("FlashCount"));
+ if (mPeriod <= 0)
+ {
+ mPeriod = LLUI::sSettingGroups["config"]->getF32("FlashPeriod");
+ }
+}
+
+void LLFlashTimer::unset()
+{
+ mUnset = true;
+ mCallback = NULL;
+}
+
+BOOL LLFlashTimer::tick()
+{
+ mIsCurrentlyHighlighted = !mIsCurrentlyHighlighted;
+
+ if (mCallback)
+ {
+ mCallback(mIsCurrentlyHighlighted);
+ }
+
+ if (++mCurrentTickCount >= mFlashCount)
+ {
+ stopFlashing();
+ }
+
+ return mUnset;
+}
+
+void LLFlashTimer::startFlashing()
+{
+ mIsFlashingInProgress = true;
+ mIsCurrentlyHighlighted = true;
+ mEventTimer.start();
+}
+
+void LLFlashTimer::stopFlashing()
+{
+ mEventTimer.stop();
+ mIsFlashingInProgress = false;
+ mIsCurrentlyHighlighted = false;
+ mCurrentTickCount = 0;
+}
+
+bool LLFlashTimer::isFlashingInProgress()
+{
+ return mIsFlashingInProgress;
+}
+
+bool LLFlashTimer::isCurrentlyHighlighted()
+{
+ return mIsCurrentlyHighlighted;
+}
+
+
diff --git a/indra/llui/llflashtimer.h b/indra/llui/llflashtimer.h
new file mode 100755
index 0000000000..db8d49f009
--- /dev/null
+++ b/indra/llui/llflashtimer.h
@@ -0,0 +1,74 @@
+/**
+ * @file llflashtimer.h
+ * @brief LLFlashTimer class implementation
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2012, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_FLASHTIMER_H
+#define LL_FLASHTIMER_H
+
+#include "lleventtimer.h"
+#include "boost/function.hpp"
+
+class LLFlashTimer : public LLEventTimer
+{
+public:
+
+ typedef boost::function<void (bool)> callback_t;
+
+ /**
+ * Constructor.
+ *
+ * @param count - how many times callback should be called (twice to not change original state)
+ * @param period - how frequently callback should be called
+ * @param cb - callback to be called each tick
+ */
+ LLFlashTimer(callback_t cb = NULL, S32 count = 0, F32 period = 0.0);
+ ~LLFlashTimer() {};
+
+ /*virtual*/ BOOL tick();
+
+ void startFlashing();
+ void stopFlashing();
+
+ bool isFlashingInProgress();
+ bool isCurrentlyHighlighted();
+ /*
+ * Use this instead of deleting this object.
+ * The next call to tick() will return true and that will destroy this object.
+ */
+ void unset();
+
+private:
+ callback_t mCallback;
+ /**
+ * How many times parent will blink.
+ */
+ S32 mFlashCount;
+ S32 mCurrentTickCount;
+ bool mIsCurrentlyHighlighted;
+ bool mIsFlashingInProgress;
+ bool mUnset;
+};
+
+#endif /* LL_FLASHTIMER_H */
diff --git a/indra/llui/llflatlistview.cpp b/indra/llui/llflatlistview.cpp
index c57c02f4b1..299f5e42d4 100644..100755
--- a/indra/llui/llflatlistview.cpp
+++ b/indra/llui/llflatlistview.cpp
@@ -87,9 +87,6 @@ bool LLFlatListView::addItem(LLPanel * item, const LLSD& value /*= LLUUID::null*
mItemsPanel->addChild(item);
break;
default:
- LL_WARNS("") << "Unsupported position." << LL_ENDL;
- delete new_pair;
- return false;
break;
}
@@ -350,7 +347,7 @@ void LLFlatListView::sort()
{
if (!mItemComparator)
{
- llwarns << "No comparator specified for sorting FlatListView items." << llendl;
+ LL_WARNS() << "No comparator specified for sorting FlatListView items." << LL_ENDL;
return;
}
@@ -523,7 +520,7 @@ void LLFlatListView::onItemMouseClick(item_pair_t* item_pair, MASK mask)
if (!item_pair->first)
{
- llwarning("Attempt to selet an item pair containing null panel item", 0);
+ LL_WARNS() << "Attempt to selet an item pair containing null panel item" << LL_ENDL;
return;
}
diff --git a/indra/llui/llflatlistview.h b/indra/llui/llflatlistview.h
index 0515853698..92bf429031 100644..100755
--- a/indra/llui/llflatlistview.h
+++ b/indra/llui/llflatlistview.h
@@ -450,8 +450,9 @@ private:
*/
class LLFlatListViewEx : public LLFlatListView
{
- LOG_CLASS(LLFlatListViewEx);
public:
+ LOG_CLASS(LLFlatListViewEx);
+
struct Params : public LLInitParam::Block<Params, LLFlatListView::Params>
{
/**
diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp
index c425782715..14f75a2352 100644..100755
--- a/indra/llui/llfloater.cpp
+++ b/indra/llui/llfloater.cpp
@@ -29,7 +29,7 @@
// mini-map floater, etc.
#include "linden_common.h"
-
+#include "llviewereventrecorder.h"
#include "llfloater.h"
#include "llfocusmgr.h"
@@ -58,10 +58,25 @@
#include "llhelp.h"
#include "llmultifloater.h"
#include "llsdutil.h"
+#include <boost/foreach.hpp>
+
// use this to control "jumping" behavior when Ctrl-Tabbing
const S32 TABBED_FLOATER_OFFSET = 0;
+extern LLControlGroup gSavedSettings;
+
+namespace LLInitParam
+{
+ void TypeValues<LLFloaterEnums::EOpenPositioning>::declareValues()
+ {
+ declare("relative", LLFloaterEnums::POSITIONING_RELATIVE);
+ declare("cascading", LLFloaterEnums::POSITIONING_CASCADING);
+ declare("centered", LLFloaterEnums::POSITIONING_CENTERED);
+ declare("specified", LLFloaterEnums::POSITIONING_SPECIFIED);
+ }
+}
+
std::string LLFloater::sButtonNames[BUTTON_COUNT] =
{
"llfloater_close_btn", //BUTTON_CLOSE
@@ -100,7 +115,6 @@ LLFloater::click_callback LLFloater::sButtonCallbacks[BUTTON_COUNT] =
LLMultiFloater* LLFloater::sHostp = NULL;
BOOL LLFloater::sQuitting = FALSE; // Flag to prevent storing visibility controls while quitting
-LLFloater::handle_map_t LLFloater::sFloaterMap;
LLFloaterView* gFloaterView = NULL;
@@ -119,7 +133,7 @@ bool LLFloater::KeyCompare::compare(const LLSD& a, const LLSD& b)
{
if (a.type() != b.type())
{
- //llerrs << "Mismatched LLSD types: (" << a << ") mismatches (" << b << ")" << llendl;
+ //LL_ERRS() << "Mismatched LLSD types: (" << a << ") mismatches (" << b << ")" << LL_ENDL;
return false;
}
else if (a.isUndefined())
@@ -154,7 +168,7 @@ LLFloater::Params::Params()
: title("title"),
short_title("short_title"),
single_instance("single_instance", false),
- auto_tile("auto_tile", false),
+ reuse_instance("reuse_instance", false),
can_resize("can_resize", false),
can_minimize("can_minimize", true),
can_close("can_close", true),
@@ -164,7 +178,8 @@ LLFloater::Params::Params()
save_rect("save_rect", false),
save_visibility("save_visibility", false),
can_dock("can_dock", false),
- open_centered("open_centered", false),
+ show_title("show_title", true),
+ positioning("positioning", LLFloaterEnums::POSITIONING_RELATIVE),
header_height("header_height", 0),
legacy_header_height("legacy_header_height", 0),
close_image("close_image"),
@@ -180,9 +195,10 @@ LLFloater::Params::Params()
dock_pressed_image("dock_pressed_image"),
help_pressed_image("help_pressed_image"),
open_callback("open_callback"),
- close_callback("close_callback")
+ close_callback("close_callback"),
+ follows("follows")
{
- visible = false;
+ changeDefault(visible, false);
}
@@ -226,13 +242,14 @@ LLFloater::LLFloater(const LLSD& key, const LLFloater::Params& p)
mTitle(p.title),
mShortTitle(p.short_title),
mSingleInstance(p.single_instance),
+ mReuseInstance(p.reuse_instance.isProvided() ? p.reuse_instance : p.single_instance), // reuse single-instance floaters by default
mKey(key),
- 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),
+ mPositioning(p.positioning),
mMinWidth(p.min_width),
mMinHeight(p.min_height),
mHeaderHeight(p.header_height),
@@ -251,7 +268,7 @@ LLFloater::LLFloater(const LLSD& key, const LLFloater::Params& p)
mMinimizeSignal(NULL)
// mNotificationContext(NULL)
{
- mHandle.bind(this);
+ mPosition.setFloater(*this);
// mNotificationContext = new LLFloaterNotificationContext(getHandle());
// Clicks stop here.
@@ -306,9 +323,6 @@ void LLFloater::initFloater(const Params& p)
// Floaters are created in the invisible state
setVisible(FALSE);
- // add self to handle->floater map
- sFloaterMap[mHandle] = this;
-
if (!getParent())
{
gFloaterView->addChild(this);
@@ -459,15 +473,24 @@ void LLFloater::layoutResizeCtrls()
mResizeHandle[3]->setRect(rect);
}
-void LLFloater::enableResizeCtrls(bool enable)
+void LLFloater::enableResizeCtrls(bool enable, bool width, bool height)
{
+ mResizeBar[LLResizeBar::LEFT]->setVisible(enable && width);
+ mResizeBar[LLResizeBar::LEFT]->setEnabled(enable && width);
+
+ mResizeBar[LLResizeBar::TOP]->setVisible(enable && height);
+ mResizeBar[LLResizeBar::TOP]->setEnabled(enable && height);
+
+ mResizeBar[LLResizeBar::RIGHT]->setVisible(enable && width);
+ mResizeBar[LLResizeBar::RIGHT]->setEnabled(enable && width);
+
+ mResizeBar[LLResizeBar::BOTTOM]->setVisible(enable && height);
+ mResizeBar[LLResizeBar::BOTTOM]->setEnabled(enable && height);
+
for (S32 i = 0; i < 4; ++i)
{
- mResizeBar[i]->setVisible(enable);
- mResizeBar[i]->setEnabled(enable);
-
- mResizeHandle[i]->setVisible(enable);
- mResizeHandle[i]->setEnabled(enable);
+ mResizeHandle[i]->setVisible(enable && width && height);
+ mResizeHandle[i]->setEnabled(enable && width && height);
}
}
@@ -484,30 +507,17 @@ LLFloater::~LLFloater()
{
LLFloaterReg::removeInstance(mInstanceName, mKey);
-// delete mNotificationContext;
-// mNotificationContext = NULL;
-
- //// am I not hosted by another floater?
- //if (mHostHandle.isDead())
- //{
- // LLFloaterView* parent = (LLFloaterView*) getParent();
-
- // if( parent )
- // {
- // parent->removeChild( this );
- // }
- //}
-
- // Just in case we might still have focus here, release it.
- releaseFocus();
+ if( gFocusMgr.childHasKeyboardFocus(this))
+ {
+ // Just in case we might still have focus here, release it.
+ releaseFocus();
+ }
// This is important so that floaters with persistent rects (i.e., those
// created with rect control rather than an LLRect) are restored in their
// correct, non-minimized positions.
setMinimized( FALSE );
- sFloaterMap.erase(mHandle);
-
delete mDragHandle;
for (S32 i = 0; i < 4; i++)
{
@@ -515,20 +525,26 @@ LLFloater::~LLFloater()
delete mResizeHandle[i];
}
- storeRectControl();
setVisible(false); // We're not visible if we're destroyed
storeVisibilityControl();
storeDockStateControl();
-
delete mMinimizeSignal;
}
void LLFloater::storeRectControl()
{
- if( mRectControl.size() > 1 )
+ if (!mRectControl.empty())
{
getControlGroup()->setRect( mRectControl, getRect() );
}
+ if (!mPosXControl.empty() && mPositioning == LLFloaterEnums::POSITIONING_RELATIVE)
+ {
+ getControlGroup()->setF32( mPosXControl, mPosition.mX );
+ }
+ if (!mPosYControl.empty() && mPositioning == LLFloaterEnums::POSITIONING_RELATIVE)
+ {
+ getControlGroup()->setF32( mPosYControl, mPosition.mY );
+ }
}
void LLFloater::storeVisibilityControl()
@@ -547,23 +563,6 @@ void LLFloater::storeDockStateControl()
}
}
-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)
{
@@ -587,7 +586,7 @@ LLControlGroup* LLFloater::getControlGroup()
void LLFloater::setVisible( BOOL visible )
{
- LLPanel::setVisible(visible); // calls handleVisibilityChange()
+ LLPanel::setVisible(visible); // calls onVisibilityChange()
if( visible && mFirstLook )
{
mFirstLook = FALSE;
@@ -618,20 +617,34 @@ void LLFloater::setVisible( BOOL visible )
storeVisibilityControl();
}
+
+void LLFloater::setIsSingleInstance(BOOL is_single_instance)
+{
+ mSingleInstance = is_single_instance;
+ if (!mIsReuseInitialized)
+ {
+ mReuseInstance = is_single_instance; // reuse single-instance floaters by default
+ }
+}
+
+
// virtual
-void LLFloater::handleVisibilityChange ( BOOL new_visibility )
+void LLFloater::onVisibilityChange ( BOOL new_visibility )
{
if (new_visibility)
{
if (getHost())
getHost()->setFloaterFlashing(this, FALSE);
}
- LLPanel::handleVisibilityChange ( new_visibility );
+ LLPanel::onVisibilityChange ( new_visibility );
}
void LLFloater::openFloater(const LLSD& key)
{
- llinfos << "Opening floater " << getName() << llendl;
+ LL_INFOS() << "Opening floater " << getName() << " full path: " << getPathname() << LL_ENDL;
+
+ LLViewerEventRecorder::instance().logVisibilityChange( getPathname(), getName(), true,"floater"); // Last param is event subtype or empty string
+
mKey = key; // in case we need to open ourselves again
if (getSoundFlags() != SILENT
@@ -640,7 +653,13 @@ void LLFloater::openFloater(const LLSD& key)
&& !getFloaterHost()
&& (!getVisible() || isMinimized()))
{
- make_ui_sound("UISndWindowOpen");
+ //Don't play a sound for incoming voice call based upon chat preference setting
+ bool playSound = !(getName() == "incoming call" && gSavedSettings.getBOOL("PlaySoundIncomingVoiceCall") == FALSE);
+
+ if(playSound)
+ {
+ make_ui_sound("UISndWindowOpen");
+ }
}
//RN: for now, we don't allow rehosting from one multifloater to another
@@ -660,6 +679,12 @@ void LLFloater::openFloater(const LLSD& key)
}
else
{
+ LLFloater* floater_to_stack = LLFloaterReg::getLastFloaterInGroup(mInstanceName);
+ if (!floater_to_stack)
+ {
+ floater_to_stack = LLFloaterReg::getLastFloaterCascading();
+ }
+ applyControlsAndPosition(floater_to_stack);
setMinimized(FALSE);
setVisibleAndFrontmost(mAutoFocus);
}
@@ -672,7 +697,8 @@ void LLFloater::openFloater(const LLSD& key)
void LLFloater::closeFloater(bool app_quitting)
{
- llinfos << "Closing floater " << getName() << llendl;
+ LL_INFOS() << "Closing floater " << getName() << LL_ENDL;
+ LLViewerEventRecorder::instance().logVisibilityChange( getPathname(), getName(), false,"floater"); // Last param is event subtype or empty string
if (app_quitting)
{
LLFloater::sQuitting = true;
@@ -698,6 +724,33 @@ void LLFloater::closeFloater(bool app_quitting)
make_ui_sound("UISndWindowClose");
}
+ gFocusMgr.clearLastFocusForGroup(this);
+
+ if (hasFocus())
+ {
+ // Do this early, so UI controls will commit before the
+ // window is taken down.
+ releaseFocus();
+
+ // give focus to dependee floater if it exists, and we had focus first
+ if (isDependent())
+ {
+ LLFloater* dependee = mDependeeHandle.get();
+ if (dependee && !dependee->isDead())
+ {
+ dependee->setFocus(TRUE);
+ }
+ }
+ }
+
+
+ //If floater is a dependent, remove it from parent (dependee)
+ LLFloater* dependee = mDependeeHandle.get();
+ if (dependee)
+ {
+ dependee->removeDependentFloater(this);
+ }
+
// now close dependent floater
for(handle_set_iter_t dependent_it = mDependents.begin();
dependent_it != mDependents.end(); )
@@ -716,24 +769,6 @@ void LLFloater::closeFloater(bool app_quitting)
}
cleanupHandles();
- gFocusMgr.clearLastFocusForGroup(this);
-
- if (hasFocus())
- {
- // Do this early, so UI controls will commit before the
- // window is taken down.
- releaseFocus();
-
- // give focus to dependee floater if it exists, and we had focus first
- if (isDependent())
- {
- LLFloater* dependee = mDependeeHandle.get();
- if (dependee && !dependee->isDead())
- {
- dependee->setFocus(TRUE);
- }
- }
- }
dirtyRect();
@@ -752,21 +787,41 @@ void LLFloater::closeFloater(bool app_quitting)
else
{
setVisible(FALSE);
+ if (!mReuseInstance)
+ {
+ destroy();
+ }
}
}
else
{
- setVisible(FALSE); // hide before destroying (so handleVisibilityChange() gets called)
- destroy();
+ setVisible(FALSE); // hide before destroying (so onVisibilityChange() gets called)
+ if (!mReuseInstance)
+ {
+ destroy();
+ }
}
}
}
/*virtual*/
+void LLFloater::closeHostedFloater()
+{
+ // When toggling *visibility*, close the host instead of the floater when hosted
+ if (getHost())
+ {
+ getHost()->closeFloater();
+ }
+ else
+ {
+ closeFloater();
+ }
+}
+
+/*virtual*/
void LLFloater::reshape(S32 width, S32 height, BOOL called_from_parent)
{
LLPanel::reshape(width, height, called_from_parent);
- storeRectControl();
}
void LLFloater::releaseFocus()
@@ -823,43 +878,153 @@ LLMultiFloater* LLFloater::getHost()
return (LLMultiFloater*)mHostHandle.get();
}
-void LLFloater::applySavedVariables()
+void LLFloater::applyControlsAndPosition(LLFloater* other)
{
- applyRectControl();
- applyDockState();
+ if (!applyDockState())
+ {
+ if (!applyRectControl())
+ {
+ applyPositioning(other, true);
+ }
+ }
}
-void LLFloater::applyRectControl()
+bool LLFloater::applyRectControl()
{
- // first, center on screen if requested
- if (mOpenCentered)
+ bool saved_rect = false;
+
+ LLRect screen_rect = calcScreenRect();
+ mPosition = LLCoordGL(screen_rect.getCenterX(), screen_rect.getCenterY()).convert();
+
+ LLFloater* last_in_group = LLFloaterReg::getLastFloaterInGroup(mInstanceName);
+ if (last_in_group && last_in_group != this)
{
- center();
+ // other floaters in our group, position ourselves relative to them and don't save the rect
+ mRectControl.clear();
+ mPositioning = LLFloaterEnums::POSITIONING_CASCADE_GROUP;
}
-
- // override center if we have saved rect control
- if (mRectControl.size() > 1)
+ else
{
- const LLRect& rect = getControlGroup()->getRect(mRectControl);
- if (rect.getWidth() > 0 && rect.getHeight() > 0)
+ bool rect_specified = false;
+ if (!mRectControl.empty())
{
- translate( rect.mLeft - getRect().mLeft, rect.mBottom - getRect().mBottom);
- if (mResizable)
+ // If we have a saved rect, use it
+ const LLRect& rect = getControlGroup()->getRect(mRectControl);
+ if (rect.notEmpty()) saved_rect = true;
+ if (saved_rect)
{
- reshape(llmax(mMinWidth, rect.getWidth()), llmax(mMinHeight, rect.getHeight()));
+ setOrigin(rect.mLeft, rect.mBottom);
+
+ if (mResizable)
+ {
+ reshape(llmax(mMinWidth, rect.getWidth()), llmax(mMinHeight, rect.getHeight()));
+ }
+ mPositioning = LLFloaterEnums::POSITIONING_RELATIVE;
+ LLRect screen_rect = calcScreenRect();
+ mPosition = LLCoordGL(screen_rect.getCenterX(), screen_rect.getCenterY()).convert();
+ rect_specified = true;
}
}
+
+ LLControlVariablePtr x_control = getControlGroup()->getControl(mPosXControl);
+ LLControlVariablePtr y_control = getControlGroup()->getControl(mPosYControl);
+ if (x_control.notNull()
+ && y_control.notNull()
+ && !x_control->isDefault()
+ && !y_control->isDefault())
+ {
+ mPosition.mX = x_control->getValue().asReal();
+ mPosition.mY = y_control->getValue().asReal();
+ mPositioning = LLFloaterEnums::POSITIONING_RELATIVE;
+ applyRelativePosition();
+
+ saved_rect = true;
+ }
+
+ // remember updated position
+ if (rect_specified)
+ {
+ storeRectControl();
+ }
+ }
+
+ if (saved_rect)
+ {
+ // propagate any derived positioning data back to settings file
+ storeRectControl();
}
+
+
+ return saved_rect;
}
-void LLFloater::applyDockState()
+bool LLFloater::applyDockState()
{
+ bool docked = false;
+
if (mDocStateControl.size() > 1)
{
- bool dockState = getControlGroup()->getBOOL(mDocStateControl);
- setDocked(dockState);
+ docked = getControlGroup()->getBOOL(mDocStateControl);
+ setDocked(docked);
}
+ return docked;
+}
+
+void LLFloater::applyPositioning(LLFloater* other, bool on_open)
+{
+ // Otherwise position according to the positioning code
+ switch (mPositioning)
+ {
+ case LLFloaterEnums::POSITIONING_CENTERED:
+ center();
+ break;
+
+ case LLFloaterEnums::POSITIONING_SPECIFIED:
+ break;
+
+ case LLFloaterEnums::POSITIONING_CASCADING:
+ if (!on_open)
+ {
+ applyRelativePosition();
+ }
+ // fall through
+ case LLFloaterEnums::POSITIONING_CASCADE_GROUP:
+ if (on_open)
+ {
+ if (other != NULL && other != this)
+ {
+ stackWith(*other);
+ }
+ else
+ {
+ static const U32 CASCADING_FLOATER_HOFFSET = 0;
+ static const U32 CASCADING_FLOATER_VOFFSET = 0;
+
+ const LLRect& snap_rect = gFloaterView->getSnapRect();
+
+ const S32 horizontal_offset = CASCADING_FLOATER_HOFFSET;
+ const S32 vertical_offset = snap_rect.getHeight() - CASCADING_FLOATER_VOFFSET;
+
+ S32 rect_height = getRect().getHeight();
+ setOrigin(horizontal_offset, vertical_offset - rect_height);
+
+ translate(snap_rect.mLeft, snap_rect.mBottom);
+ }
+ setFollows(FOLLOWS_TOP | FOLLOWS_LEFT);
+ }
+ break;
+
+ case LLFloaterEnums::POSITIONING_RELATIVE:
+ {
+ applyRelativePosition();
+
+ break;
+ }
+ default:
+ // Do nothing
+ break;
+ }
}
void LLFloater::applyTitle()
@@ -929,14 +1094,16 @@ BOOL LLFloater::canSnapTo(const LLView* other_view)
{
if (NULL == other_view)
{
- llwarns << "other_view is NULL" << llendl;
+ LL_WARNS() << "other_view is NULL" << LL_ENDL;
return FALSE;
}
if (other_view != getParent())
{
const LLFloater* other_floaterp = dynamic_cast<const LLFloater*>(other_view);
- if (other_floaterp && other_floaterp->getSnapTarget() == getHandle() && mDependents.find(other_floaterp->getHandle()) != mDependents.end())
+ if (other_floaterp
+ && other_floaterp->getSnapTarget() == getHandle()
+ && mDependents.find(other_floaterp->getHandle()) != mDependents.end())
{
// this is a dependent that is already snapped to us, so don't snap back to it
return FALSE;
@@ -968,9 +1135,30 @@ void LLFloater::handleReshape(const LLRect& new_rect, bool by_user)
const LLRect old_rect = getRect();
LLView::handleReshape(new_rect, by_user);
+ if (by_user && !getHost())
+ {
+ LLFloaterView * floaterVp = dynamic_cast<LLFloaterView*>(getParent());
+ if (floaterVp)
+ {
+ floaterVp->adjustToFitScreen(this, !isMinimized());
+ }
+ }
+
// if not minimized, adjust all snapped dependents to new shape
if (!isMinimized())
{
+ if (by_user)
+ {
+ if (isDocked())
+ {
+ setDocked( false, false);
+ }
+ storeRectControl();
+ mPositioning = LLFloaterEnums::POSITIONING_RELATIVE;
+ LLRect screen_rect = calcScreenRect();
+ mPosition = LLCoordGL(screen_rect.getCenterX(), screen_rect.getCenterY()).convert();
+ }
+
// gather all snapped dependents
for(handle_set_iter_t dependent_it = mDependents.begin();
dependent_it != mDependents.end(); ++dependent_it)
@@ -1025,7 +1213,7 @@ void LLFloater::setMinimized(BOOL minimize)
if (minimize == mMinimized) return;
- if(mMinimizeSignal)
+ if (mMinimizeSignal)
{
(*mMinimizeSignal)(this, LLSD(minimize));
}
@@ -1034,7 +1222,6 @@ void LLFloater::setMinimized(BOOL minimize)
{
// minimized flag should be turned on before release focus
mMinimized = TRUE;
-
mExpandedRect = getRect();
// If the floater has been dragged while minimized in the
@@ -1057,10 +1244,6 @@ void LLFloater::setMinimized(BOOL minimize)
mButtonsEnabled[BUTTON_RESTORE] = TRUE;
}
- if (mDragHandle)
- {
- mDragHandle->setVisible(TRUE);
- }
setBorderVisible(TRUE);
for(handle_set_iter_t dependent_it = mDependents.begin();
@@ -1111,7 +1294,6 @@ void LLFloater::setMinimized(BOOL minimize)
}
setOrigin( mExpandedRect.mLeft, mExpandedRect.mBottom );
-
if (mButtonsEnabled[BUTTON_RESTORE])
{
mButtonsEnabled[BUTTON_MINIMIZE] = TRUE;
@@ -1160,7 +1342,7 @@ void LLFloater::setFocus( BOOL b )
{
return;
}
- LLUICtrl* last_focus = gFocusMgr.getLastFocusForGroup(this);
+ LLView* last_focus = gFocusMgr.getLastFocusForGroup(this);
// a descendent already has focus
BOOL child_had_focus = hasFocus();
@@ -1170,7 +1352,8 @@ void LLFloater::setFocus( BOOL b )
if (b)
{
// only push focused floaters to front of stack if not in midst of ctrl-tab cycle
- if (!getHost() && !((LLFloaterView*)getParent())->getCycleMode())
+ LLFloaterView * parent = dynamic_cast<LLFloaterView *>(getParent());
+ if (!getHost() && parent && !parent->getCycleMode())
{
if (!isFrontmost())
{
@@ -1211,19 +1394,9 @@ void LLFloater::setIsChrome(BOOL is_chrome)
mButtons[BUTTON_CLOSE]->setToolTip(LLStringExplicit(getButtonTooltip(Params(), BUTTON_CLOSE, is_chrome)));
}
- // no titles displayed on "chrome" floaters
- if (mDragHandle)
- mDragHandle->setTitleVisible(!is_chrome);
-
LLPanel::setIsChrome(is_chrome);
}
-void LLFloater::setTitleVisible(bool visible)
-{
- if (mDragHandle)
- mDragHandle->setTitleVisible(visible);
-}
-
// Change the draw style to account for the foreground state.
void LLFloater::setForeground(BOOL front)
{
@@ -1278,7 +1451,6 @@ void LLFloater::setHost(LLMultiFloater* host)
mButtonScale = 1.f;
//mButtonsEnabled[BUTTON_TEAR_OFF] = FALSE;
}
- updateTitleButtons();
if (host)
{
mHostHandle = host->getHandle();
@@ -1288,6 +1460,8 @@ void LLFloater::setHost(LLMultiFloater* host)
{
mHostHandle.markDead();
}
+
+ updateTitleButtons();
}
void LLFloater::moveResizeHandlesToFront()
@@ -1309,9 +1483,13 @@ void LLFloater::moveResizeHandlesToFront()
}
}
+/*virtual*/
BOOL LLFloater::isFrontmost()
{
- return gFloaterView && gFloaterView->getFrontmost() == this && getVisible();
+ LLFloaterView* floater_view = getParentByType<LLFloaterView>();
+ return getVisible()
+ && (floater_view
+ && floater_view->getFrontmost() == this);
}
void LLFloater::addDependentFloater(LLFloater* floaterp, BOOL reposition)
@@ -1324,7 +1502,7 @@ void LLFloater::addDependentFloater(LLFloater* floaterp, BOOL reposition)
floaterp->setRect(gFloaterView->findNeighboringPosition(this, floaterp));
floaterp->setSnapTarget(getHandle());
}
- gFloaterView->adjustToFitScreen(floaterp, FALSE);
+ gFloaterView->adjustToFitScreen(floaterp, FALSE, TRUE);
if (floaterp->isFrontmost())
{
// make sure to bring self and sibling floaters to front
@@ -1373,6 +1551,17 @@ BOOL LLFloater::handleScrollWheel(S32 x, S32 y, S32 clicks)
}
// virtual
+BOOL LLFloater::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ LL_DEBUGS() << "LLFloater::handleMouseUp calling LLPanel (really LLView)'s handleMouseUp (first initialized xui to: " << getPathname() << " )" << LL_ENDL;
+ BOOL handled = LLPanel::handleMouseUp(x,y,mask); // Not implemented in LLPanel so this actually calls LLView
+ if (handled) {
+ LLViewerEventRecorder::instance().updateMouseEventInfo(x,y,-55,-55,getPathname());
+ }
+ return handled;
+}
+
+// virtual
BOOL LLFloater::handleMouseDown(S32 x, S32 y, MASK mask)
{
if( mMinimized )
@@ -1384,6 +1573,7 @@ BOOL LLFloater::handleMouseDown(S32 x, S32 y, MASK mask)
if(offerClickToButton(x, y, mask, BUTTON_CLOSE)) return TRUE;
if(offerClickToButton(x, y, mask, BUTTON_RESTORE)) return TRUE;
if(offerClickToButton(x, y, mask, BUTTON_TEAR_OFF)) return TRUE;
+ if(offerClickToButton(x, y, mask, BUTTON_DOCK)) return TRUE;
// Otherwise pass to drag handle for movement
return mDragHandle->handleMouseDown(x, y, mask);
@@ -1391,7 +1581,11 @@ BOOL LLFloater::handleMouseDown(S32 x, S32 y, MASK mask)
else
{
bringToFront( x, y );
- return LLPanel::handleMouseDown( x, y, mask );
+ BOOL handled = LLPanel::handleMouseDown( x, y, mask );
+ if (handled) {
+ LLViewerEventRecorder::instance().updateMouseEventInfo(x,y,-55,-55,getPathname());
+ }
+ return handled;
}
}
@@ -1429,7 +1623,7 @@ void LLFloater::bringToFront( S32 x, S32 y )
}
else
{
- LLFloaterView* parent = (LLFloaterView*) getParent();
+ LLFloaterView* parent = dynamic_cast<LLFloaterView*>( getParent() );
if (parent)
{
parent->bringToFront( this );
@@ -1440,10 +1634,19 @@ void LLFloater::bringToFront( S32 x, S32 y )
// virtual
-void LLFloater::setVisibleAndFrontmost(BOOL take_focus)
+void LLFloater::setVisibleAndFrontmost(BOOL take_focus,const LLSD& key)
{
- setVisible(TRUE);
- setFrontmost(take_focus);
+ LLMultiFloater* hostp = getHost();
+ if (hostp)
+ {
+ hostp->setVisible(TRUE);
+ hostp->setFrontmost(take_focus);
+ }
+ else
+ {
+ setVisible(TRUE);
+ setFrontmost(take_focus);
+ }
}
void LLFloater::setFrontmost(BOOL take_focus)
@@ -1459,7 +1662,11 @@ void LLFloater::setFrontmost(BOOL take_focus)
{
// there are more than one floater view
// so we need to query our parent directly
- ((LLFloaterView*)getParent())->bringToFront(this, take_focus);
+ LLFloaterView * parent = dynamic_cast<LLFloaterView*>( getParent() );
+ if (parent)
+ {
+ parent->bringToFront(this, take_focus);
+ }
// Make sure to set the appropriate transparency type (STORM-732).
updateTransparency(hasFocus() || getIsChrome() ? TT_ACTIVE : TT_INACTIVE);
@@ -1489,6 +1696,13 @@ void LLFloater::setDocked(bool docked, bool pop_on_undock)
{
mDocked = docked;
mButtonsEnabled[BUTTON_DOCK] = !mDocked;
+
+ if (mDocked)
+ {
+ setMinimized(FALSE);
+ mPositioning = LLFloaterEnums::POSITIONING_RELATIVE;
+ }
+
updateTitleButtons();
storeDockStateControl();
@@ -1518,10 +1732,12 @@ void LLFloater::onClickTearOff(LLFloater* self)
gFloaterView->addChild(self);
self->openFloater(self->getKey());
-
- // only force position for floaters that don't have that data saved
- if (self->mRectControl.size() <= 1)
+ if (self->mSaveRect && !self->mRectControl.empty())
{
+ self->applyRectControl();
+ }
+ else
+ { // only force position for floaters that don't have that data saved
new_rect.setLeftTopAndSize(host_floater->getRect().mLeft + 5, host_floater->getRect().mTop - floater_header_size - 5, self->getRect().getWidth(), self->getRect().getHeight());
self->setRect(new_rect);
}
@@ -1535,6 +1751,10 @@ void LLFloater::onClickTearOff(LLFloater* self)
LLMultiFloater* new_host = (LLMultiFloater*)self->mLastHostHandle.get();
if (new_host)
{
+ if (self->mSaveRect)
+ {
+ self->storeRectControl();
+ }
self->setMinimized(FALSE); // to reenable minimize button if it was minimized
new_host->showFloater(self);
// make sure host is visible
@@ -1543,6 +1763,7 @@ void LLFloater::onClickTearOff(LLFloater* self)
self->setTornOff(false);
}
self->updateTitleButtons();
+ self->setOpenPositioning(LLFloaterEnums::POSITIONING_RELATIVE);
}
// static
@@ -1568,55 +1789,22 @@ void LLFloater::onClickHelp( LLFloater* self )
}
}
-// static
-LLFloater* LLFloater::getClosableFloaterFromFocus()
+void LLFloater::initRectControl()
{
- LLFloater* focused_floater = NULL;
-
- handle_map_iter_t iter;
- for(iter = sFloaterMap.begin(); iter != sFloaterMap.end(); ++iter)
+ // save_rect and save_visibility only apply to registered floaters
+ if (mSaveRect)
{
- focused_floater = iter->second;
- if (focused_floater->hasFocus())
- {
- break;
- }
- }
-
- if (iter == sFloaterMap.end())
- {
- // nothing found, return
- return NULL;
- }
-
- // The focused floater may not be closable,
- // Find and close a parental floater that is closeable, if any.
- LLFloater* prev_floater = NULL;
- for(LLFloater* floater_to_close = focused_floater;
- NULL != floater_to_close;
- floater_to_close = gFloaterView->getParentFloater(floater_to_close))
- {
- if(floater_to_close->isCloseable())
- {
- 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;
+ std::string ctrl_name = getControlName(mInstanceName, mKey);
+ mRectControl = LLFloaterReg::declareRectControl(ctrl_name);
+ mPosXControl = LLFloaterReg::declarePosXControl(ctrl_name);
+ mPosYControl = LLFloaterReg::declarePosYControl(ctrl_name);
}
-
- return NULL;
}
// static
-void LLFloater::closeFocusedFloater()
+void LLFloater::closeFrontmostFloater()
{
- LLFloater* floater_to_close = LLFloater::getClosableFloaterFromFocus();
+ LLFloater* floater_to_close = gFloaterView->getFrontmostClosableFloater();
if(floater_to_close)
{
floater_to_close->closeFloater();
@@ -1641,7 +1829,7 @@ void LLFloater::onClickClose( LLFloater* self )
self->onClickCloseBtn();
}
-void LLFloater::onClickCloseBtn()
+void LLFloater::onClickCloseBtn(bool app_quitting)
{
closeFloater(false);
}
@@ -1698,7 +1886,7 @@ void LLFloater::draw()
const LLFontGL* font = LLFontGL::getFontSansSerif();
LLRect r = getRect();
- gl_rect_2d_offset_local(0, r.getHeight(), r.getWidth(), r.getHeight() - (S32)font->getLineHeight() - 1,
+ gl_rect_2d_offset_local(0, r.getHeight(), r.getWidth(), r.getHeight() - font->getLineHeight() - 1,
titlebar_focus_color % alpha, 0, TRUE);
}
}
@@ -1727,7 +1915,7 @@ void LLFloater::draw()
{
drawChild(mButtons[i]);
}
- drawChild(mDragHandle);
+ drawChild(mDragHandle, 0, 0, TRUE);
}
else
{
@@ -1766,11 +1954,12 @@ void LLFloater::drawShadow(LLPanel* panel)
}
gl_drop_shadow(left, top, right, bottom,
shadow_color % getCurrentTransparency(),
- llround(shadow_offset));
+ ll_round(shadow_offset));
}
void LLFloater::updateTransparency(LLView* view, ETypeTransparency transparency_type)
{
+ if (!view) return;
child_list_t children = *view->getChildList();
child_list_t::iterator it = children.begin();
@@ -1844,6 +2033,12 @@ void LLFloater::setCanDrag(BOOL can_drag)
}
}
+bool LLFloater::getCanDrag()
+{
+ return mDragHandle->getEnabled();
+}
+
+
void LLFloater::updateTitleButtons()
{
static LLUICachedControl<S32> floater_close_box_size ("UIFloaterCloseBoxSize", 0);
@@ -1887,16 +2082,16 @@ void LLFloater::updateTitleButtons()
btn_rect.setLeftTopAndSize(
LLPANEL_BORDER_WIDTH,
getRect().getHeight() - close_box_from_top - (floater_close_box_size + 1) * button_count,
- llround((F32)floater_close_box_size * mButtonScale),
- llround((F32)floater_close_box_size * mButtonScale));
+ ll_round((F32)floater_close_box_size * mButtonScale),
+ ll_round((F32)floater_close_box_size * mButtonScale));
}
else
{
btn_rect.setLeftTopAndSize(
getRect().getWidth() - LLPANEL_BORDER_WIDTH - (floater_close_box_size + 1) * button_count,
getRect().getHeight() - close_box_from_top,
- llround((F32)floater_close_box_size * mButtonScale),
- llround((F32)floater_close_box_size * mButtonScale));
+ ll_round((F32)floater_close_box_size * mButtonScale),
+ ll_round((F32)floater_close_box_size * mButtonScale));
}
// first time here, init 'buttons_rect'
@@ -1947,16 +2142,16 @@ void LLFloater::buildButtons(const Params& floater_params)
btn_rect.setLeftTopAndSize(
LLPANEL_BORDER_WIDTH,
getRect().getHeight() - close_box_from_top - (floater_close_box_size + 1) * (i + 1),
- llround(floater_close_box_size * mButtonScale),
- llround(floater_close_box_size * mButtonScale));
+ ll_round(floater_close_box_size * mButtonScale),
+ ll_round(floater_close_box_size * mButtonScale));
}
else
{
btn_rect.setLeftTopAndSize(
getRect().getWidth() - LLPANEL_BORDER_WIDTH - (floater_close_box_size + 1) * (i + 1),
getRect().getHeight() - close_box_from_top,
- llround(floater_close_box_size * mButtonScale),
- llround(floater_close_box_size * mButtonScale));
+ ll_round(floater_close_box_size * mButtonScale),
+ ll_round(floater_close_box_size * mButtonScale));
}
LLButton::Params p;
@@ -2048,25 +2243,21 @@ static LLDefaultChildRegistry::Register<LLFloaterView> r("floater_view");
LLFloaterView::LLFloaterView (const Params& p)
: LLUICtrl (p),
-
mFocusCycleMode(FALSE),
mMinimizePositionVOffset(0),
mSnapOffsetBottom(0),
- mSnapOffsetRight(0)
+ mSnapOffsetRight(0),
+ mFrontChild(NULL)
{
+ mSnapView = getHandle();
}
// By default, adjust vertical.
void LLFloaterView::reshape(S32 width, S32 height, BOOL called_from_parent)
{
- reshapeFloater(width, height, called_from_parent, ADJUST_VERTICAL_YES);
-}
+ LLView::reshape(width, height, called_from_parent);
-// When reshaping this view, make the floaters follow their closest edge.
-void LLFloaterView::reshapeFloater(S32 width, S32 height, BOOL called_from_parent, BOOL adjust_vertical)
-{
- S32 old_width = getRect().getWidth();
- S32 old_height = getRect().getHeight();
+ mLastSnapRect = getSnapRect();
for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
{
@@ -2074,70 +2265,52 @@ void LLFloaterView::reshapeFloater(S32 width, S32 height, BOOL called_from_paren
LLFloater* floaterp = (LLFloater*)viewp;
if (floaterp->isDependent())
{
- // dependents use same follow flags as their "dependee"
+ // dependents are moved with their "dependee"
continue;
}
- // Make if follow the edge it is closest to
- U32 follow_flags = 0x0;
-
- if (floaterp->isMinimized())
+ if (!floaterp->isMinimized() && floaterp->getCanDrag())
{
- follow_flags |= (FOLLOWS_LEFT | FOLLOWS_TOP);
- }
- else
- {
- LLRect r = floaterp->getRect();
+ LLRect old_rect = floaterp->getRect();
+ floaterp->applyPositioning(NULL, false);
+ LLRect new_rect = 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);
+ //LLRect r = floaterp->getRect();
- S32 top_offset = llabs(old_height - r.mTop);
- S32 bottom_offset = llabs(r.mBottom - 0);
+ //// Compute absolute distance from each edge of screen
+ //S32 left_offset = llabs(r.mLeft - 0);
+ //S32 right_offset = llabs(old_right - r.mRight);
+ //S32 top_offset = llabs(old_top - r.mTop);
+ //S32 bottom_offset = llabs(r.mBottom - 0);
- if (left_offset < right_offset)
- {
- follow_flags |= FOLLOWS_LEFT;
- }
- else
- {
- follow_flags |= FOLLOWS_RIGHT;
- }
+ S32 translate_x = new_rect.mLeft - old_rect.mLeft;
+ S32 translate_y = new_rect.mBottom - old_rect.mBottom;
- // "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;
- }
- }
+ //if (left_offset > right_offset)
+ //{
+ // translate_x = new_right - old_right;
+ //}
- floaterp->setFollows(follow_flags);
+ //if (top_offset < bottom_offset)
+ //{
+ // translate_y = new_top - old_top;
+ //}
- //RN: all dependent floaters copy follow behavior of "parent"
- for(LLFloater::handle_set_iter_t dependent_it = floaterp->mDependents.begin();
- dependent_it != floaterp->mDependents.end(); ++dependent_it)
- {
- LLFloater* dependent_floaterp = dependent_it->get();
- if (dependent_floaterp)
+ // don't reposition immovable floaters
+ //if (floaterp->getCanDrag())
+ //{
+ // floaterp->translate(translate_x, translate_y);
+ //}
+ BOOST_FOREACH(LLHandle<LLFloater> dependent_floater, floaterp->mDependents)
{
- dependent_floaterp->setFollows(follow_flags);
+ if (dependent_floater.get())
+ {
+ dependent_floater.get()->translate(translate_x, translate_y);
+ }
}
}
}
-
- LLView::reshape(width, height, called_from_parent);
}
@@ -2223,6 +2396,20 @@ LLRect LLFloaterView::findNeighboringPosition( LLFloater* reference_floater, LLF
void LLFloaterView::bringToFront(LLFloater* child, BOOL give_focus)
{
+ if (!child)
+ return;
+
+ if (mFrontChild == child)
+ {
+ if (give_focus && !gFocusMgr.childHasKeyboardFocus(child))
+ {
+ child->setFocus(TRUE);
+ }
+ return;
+ }
+
+ mFrontChild = child;
+
// *TODO: make this respect floater's mAutoFocus value, instead of
// using parameter
if (child->getHost())
@@ -2230,15 +2417,14 @@ void LLFloaterView::bringToFront(LLFloater* child, BOOL give_focus)
// this floater is hosted elsewhere and hence not one of our children, abort
return;
}
- std::vector<LLView*> floaters_to_move;
+ std::vector<LLFloater*> floaters_to_move;
// Look at all floaters...tab
- for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ for (child_list_const_iter_t child_it = beginChild(); child_it != endChild(); ++child_it)
{
- LLView* viewp = *child_it;
- LLFloater *floater = (LLFloater *)viewp;
+ LLFloater* floater = dynamic_cast<LLFloater*>(*child_it);
// ...but if I'm a dependent floater...
- if (child->isDependent())
+ if (floater && child->isDependent())
{
// ...look for floaters that have me as a dependent...
LLFloater::handle_set_iter_t found_dependent = floater->mDependents.find(child->getHandle());
@@ -2246,15 +2432,14 @@ void LLFloaterView::bringToFront(LLFloater* child, BOOL give_focus)
if (found_dependent != floater->mDependents.end())
{
// ...and make sure all children of that floater (including me) are brought to front...
- for(LLFloater::handle_set_iter_t dependent_it = floater->mDependents.begin();
- dependent_it != floater->mDependents.end(); )
+ for (LLFloater::handle_set_iter_t dependent_it = floater->mDependents.begin();
+ dependent_it != floater->mDependents.end(); ++dependent_it)
{
LLFloater* sibling = dependent_it->get();
if (sibling)
{
floaters_to_move.push_back(sibling);
}
- ++dependent_it;
}
//...before bringing my parent to the front...
floaters_to_move.push_back(floater);
@@ -2262,10 +2447,10 @@ void LLFloaterView::bringToFront(LLFloater* child, BOOL give_focus)
}
}
- std::vector<LLView*>::iterator view_it;
- for(view_it = floaters_to_move.begin(); view_it != floaters_to_move.end(); ++view_it)
+ std::vector<LLFloater*>::iterator floater_it;
+ for(floater_it = floaters_to_move.begin(); floater_it != floaters_to_move.end(); ++floater_it)
{
- LLFloater* floaterp = (LLFloater*)(*view_it);
+ LLFloater* floaterp = *floater_it;
sendChildToFront(floaterp);
// always unminimize dependee, but allow dependents to stay minimized
@@ -2277,23 +2462,19 @@ void LLFloaterView::bringToFront(LLFloater* child, BOOL give_focus)
floaters_to_move.clear();
// ...then bringing my own dependents to the front...
- for(LLFloater::handle_set_iter_t dependent_it = child->mDependents.begin();
- dependent_it != child->mDependents.end(); )
+ for (LLFloater::handle_set_iter_t dependent_it = child->mDependents.begin();
+ dependent_it != child->mDependents.end(); ++dependent_it)
{
LLFloater* dependent = dependent_it->get();
if (dependent)
{
sendChildToFront(dependent);
- //don't un-minimize dependent windows automatically
- // respect user's wishes
- //dependent->setMinimized(FALSE);
}
- ++dependent_it;
}
// ...and finally bringing myself to front
// (do this last, so that I'm left in front at end of this call)
- if( *getChildList()->begin() != child )
+ if (*beginChild() != child)
{
sendChildToFront(child);
}
@@ -2351,6 +2532,24 @@ void LLFloaterView::highlightFocusedFloater()
}
}
+LLFloater* LLFloaterView::getFrontmostClosableFloater()
+{
+ child_list_const_iter_t child_it;
+ LLFloater* frontmost_floater = NULL;
+
+ for ( child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ frontmost_floater = (LLFloater *)(*child_it);
+
+ if (frontmost_floater->isInVisibleChain() && frontmost_floater->isCloseable())
+ {
+ return frontmost_floater;
+ }
+ }
+
+ return NULL;
+}
+
void LLFloaterView::unhighlightFocusedFloater()
{
for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
@@ -2458,6 +2657,52 @@ void LLFloaterView::closeAllChildren(bool app_quitting)
}
}
+void LLFloaterView::hiddenFloaterClosed(LLFloater* floater)
+{
+ for (hidden_floaters_t::iterator it = mHiddenFloaters.begin(), end_it = mHiddenFloaters.end();
+ it != end_it;
+ ++it)
+ {
+ if (it->first.get() == floater)
+ {
+ it->second.disconnect();
+ mHiddenFloaters.erase(it);
+ break;
+ }
+ }
+}
+
+void LLFloaterView::hideAllFloaters()
+{
+ child_list_t child_list = *(getChildList());
+
+ for (child_list_iter_t it = child_list.begin(); it != child_list.end(); ++it)
+ {
+ LLFloater* floaterp = dynamic_cast<LLFloater*>(*it);
+ if (floaterp && floaterp->getVisible())
+ {
+ floaterp->setVisible(false);
+ boost::signals2::connection connection = floaterp->mCloseSignal.connect(boost::bind(&LLFloaterView::hiddenFloaterClosed, this, floaterp));
+ mHiddenFloaters.push_back(std::make_pair(floaterp->getHandle(), connection));
+ }
+ }
+}
+
+void LLFloaterView::showHiddenFloaters()
+{
+ for (hidden_floaters_t::iterator it = mHiddenFloaters.begin(), end_it = mHiddenFloaters.end();
+ it != end_it;
+ ++it)
+ {
+ LLFloater* floaterp = it->first.get();
+ if (floaterp)
+ {
+ floaterp->setVisible(true);
+ }
+ it->second.disconnect();
+ }
+ mHiddenFloaters.clear();
+}
BOOL LLFloaterView::allChildrenClosed()
{
@@ -2491,6 +2736,12 @@ void LLFloaterView::shiftFloaters(S32 x_offset, S32 y_offset)
void LLFloaterView::refresh()
{
+ LLRect snap_rect = getSnapRect();
+ if (snap_rect != mLastSnapRect)
+ {
+ reshape(getRect().getWidth(), getRect().getHeight(), TRUE);
+ }
+
// Constrain children to be entirely on the screen
for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
{
@@ -2503,7 +2754,7 @@ void LLFloaterView::refresh()
}
}
-void LLFloaterView::adjustToFitScreen(LLFloater* floater, BOOL allow_partial_outside)
+void LLFloaterView::adjustToFitScreen(LLFloater* floater, BOOL allow_partial_outside, BOOL snap_in_toolbars/* = false*/)
{
if (floater->getParent() != this)
{
@@ -2555,11 +2806,29 @@ void LLFloaterView::adjustToFitScreen(LLFloater* floater, BOOL allow_partial_out
}
}
+ const LLRect& floater_rect = floater->getRect();
+
+ S32 delta_left = mToolbarLeftRect.notEmpty() ? mToolbarLeftRect.mRight - floater_rect.mRight : 0;
+ S32 delta_bottom = mToolbarBottomRect.notEmpty() ? mToolbarBottomRect.mTop - floater_rect.mTop : 0;
+ S32 delta_right = mToolbarRightRect.notEmpty() ? mToolbarRightRect.mLeft - floater_rect.mLeft : 0;
+
// move window fully onscreen
- if (floater->translateIntoRect( getLocalRect(), allow_partial_outside ))
+ if (floater->translateIntoRect( snap_in_toolbars ? getSnapRect() : gFloaterView->getRect(), allow_partial_outside ? FLOATER_MIN_VISIBLE_PIXELS : S32_MAX ))
{
floater->clearSnapTarget();
}
+ else if (delta_left > 0 && floater_rect.mTop < mToolbarLeftRect.mTop && floater_rect.mBottom > mToolbarLeftRect.mBottom)
+ {
+ floater->translate(delta_left, 0);
+ }
+ else if (delta_bottom > 0 && floater_rect.mLeft > mToolbarBottomRect.mLeft && floater_rect.mRight < mToolbarBottomRect.mRight)
+ {
+ floater->translate(0, delta_bottom);
+ }
+ else if (delta_right < 0 && floater_rect.mTop < mToolbarRightRect.mTop && floater_rect.mBottom > mToolbarRightRect.mBottom)
+ {
+ floater->translate(delta_right, 0);
+ }
}
void LLFloaterView::draw()
@@ -2590,9 +2859,13 @@ void LLFloaterView::draw()
LLRect LLFloaterView::getSnapRect() const
{
- LLRect snap_rect = getRect();
- snap_rect.mBottom += mSnapOffsetBottom;
- snap_rect.mRight -= mSnapOffsetRight;
+ LLRect snap_rect = getLocalRect();
+
+ LLView* snap_view = mSnapView.get();
+ if (snap_view)
+ {
+ snap_view->localRectToOtherView(snap_view->getLocalRect(), &snap_rect, this);
+ }
return snap_rect;
}
@@ -2601,10 +2874,13 @@ LLFloater *LLFloaterView::getFocusedFloater() const
{
for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
{
- LLUICtrl* ctrlp = (*child_it)->isCtrl() ? static_cast<LLUICtrl*>(*child_it) : NULL;
- if ( ctrlp && ctrlp->hasFocus() )
+ if ((*child_it)->isCtrl())
{
- return static_cast<LLFloater *>(ctrlp);
+ LLFloater* ctrlp = dynamic_cast<LLFloater*>(*child_it);
+ if ( ctrlp && ctrlp->hasFocus() )
+ {
+ return ctrlp;
+ }
}
}
return NULL;
@@ -2679,13 +2955,6 @@ void LLFloaterView::syncFloaterTabOrder()
}
}
}
-
- // sync draw order to tab order
- for ( child_list_const_reverse_iter_t child_it = getChildList()->rbegin(); child_it != getChildList()->rend(); ++child_it)
- {
- LLFloater* floaterp = (LLFloater*)*child_it;
- moveChildToFrontOfTabGroup(floaterp);
- }
}
LLFloater* LLFloaterView::getParentFloater(LLView* viewp) const
@@ -2755,21 +3024,35 @@ void LLFloaterView::popVisibleAll(const skip_list_t& skip_list)
LLFloaterReg::blockShowFloaters(false);
}
+void LLFloaterView::setToolbarRect(LLToolBarEnums::EToolBarLocation tb, const LLRect& toolbar_rect)
+{
+ switch (tb)
+ {
+ case LLToolBarEnums::TOOLBAR_LEFT:
+ mToolbarLeftRect = toolbar_rect;
+ break;
+ case LLToolBarEnums::TOOLBAR_BOTTOM:
+ mToolbarBottomRect = toolbar_rect;
+ break;
+ case LLToolBarEnums::TOOLBAR_RIGHT:
+ mToolbarRightRect = toolbar_rect;
+ break;
+ default:
+ LL_WARNS() << "setToolbarRect() passed odd toolbar number " << (S32) tb << LL_ENDL;
+ break;
+ }
+}
+
void LLFloater::setInstanceName(const std::string& name)
{
- if (name == mInstanceName)
- return;
+ if (name != mInstanceName)
+ {
llassert_always(mInstanceName.empty());
mInstanceName = name;
if (!mInstanceName.empty())
{
std::string ctrl_name = getControlName(mInstanceName, mKey);
-
- // save_rect and save_visibility only apply to registered floaters
- if (!mRectControl.empty())
- {
- mRectControl = LLFloaterReg::declareRectControl(ctrl_name);
- }
+ initRectControl();
if (!mVisibilityControl.empty())
{
mVisibilityControl = LLFloaterReg::declareVisibilityControl(ctrl_name);
@@ -2778,9 +3061,9 @@ void LLFloater::setInstanceName(const std::string& name)
{
mDocStateControl = LLFloaterReg::declareDockStateControl(ctrl_name);
}
-
}
}
+}
void LLFloater::setKey(const LLSD& newkey)
{
@@ -2825,6 +3108,12 @@ void LLFloater::initFromParams(const LLFloater::Params& p)
// control_name, tab_stop, focus_lost_callback, initial_value, rect, enabled, visible
LLPanel::initFromParams(p);
+ // override any follows flags
+ if (mPositioning != LLFloaterEnums::POSITIONING_SPECIFIED)
+ {
+ setFollows(FOLLOWS_NONE);
+ }
+
mTitle = p.title;
mShortTitle = p.short_title;
applyTitle();
@@ -2840,18 +3129,15 @@ void LLFloater::initFromParams(const LLFloater::Params& p)
mHeaderHeight = p.header_height;
mLegacyHeaderHeight = p.legacy_header_height;
mSingleInstance = p.single_instance;
- mAutoTile = p.auto_tile;
- mOpenCentered = p.open_centered;
+ mReuseInstance = p.reuse_instance.isProvided() ? p.reuse_instance : p.single_instance;
- if (p.save_rect)
- {
- mRectControl = "t"; // flag to build mRectControl name once mInstanceName is set
- }
+ mPositioning = p.positioning;
+
+ mSaveRect = p.save_rect;
if (p.save_visibility)
{
mVisibilityControl = "t"; // flag to build mVisibilityControl name once mInstanceName is set
}
-
if(p.save_dock_state)
{
mDocStateControl = "t"; // flag to build mDocStateControl name once mInstanceName is set
@@ -2860,12 +3146,17 @@ void LLFloater::initFromParams(const LLFloater::Params& p)
// open callback
if (p.open_callback.isProvided())
{
- mOpenSignal.connect(initCommitCallback(p.open_callback));
+ setOpenCallback(initCommitCallback(p.open_callback));
}
// close callback
if (p.close_callback.isProvided())
{
- mCloseSignal.connect(initCommitCallback(p.close_callback));
+ setCloseCallback(initCommitCallback(p.close_callback));
+ }
+
+ if (mDragHandle)
+ {
+ mDragHandle->setTitleVisible(p.show_title);
}
}
@@ -2875,21 +3166,73 @@ boost::signals2::connection LLFloater::setMinimizeCallback( const commit_signal_
return mMinimizeSignal->connect(cb);
}
-LLFastTimer::DeclareTimer POST_BUILD("Floater Post Build");
+boost::signals2::connection LLFloater::setOpenCallback( const commit_signal_t::slot_type& cb )
+{
+ return mOpenSignal.connect(cb);
+}
+
+boost::signals2::connection LLFloater::setCloseCallback( const commit_signal_t::slot_type& cb )
+{
+ return mCloseSignal.connect(cb);
+}
+
+LLTrace::BlockTimerStatHandle POST_BUILD("Floater Post Build");
+static LLTrace::BlockTimerStatHandle FTM_EXTERNAL_FLOATER_LOAD("Load Extern Floater Reference");
bool LLFloater::initFloaterXML(LLXMLNodePtr node, LLView *parent, const std::string& filename, LLXMLNodePtr output_node)
{
- Params params(LLUICtrlFactory::getDefaultParams<LLFloater>());
+ Params default_params(LLUICtrlFactory::getDefaultParams<LLFloater>());
+ Params params(default_params);
+
LLXUIParser parser;
parser.readXUI(node, params, filename); // *TODO: Error checking
+ std::string xml_filename = params.filename;
+
+ if (!xml_filename.empty())
+ {
+ LLXMLNodePtr referenced_xml;
+
+ if (output_node)
+ {
+ //if we are exporting, we want to export the current xml
+ //not the referenced xml
+ Params output_params;
+ parser.readXUI(node, output_params, LLUICtrlFactory::getInstance()->getCurFileName());
+ setupParamsForExport(output_params, parent);
+ output_node->setName(node->getName()->mString);
+ parser.writeXUI(output_node, output_params, LLInitParam::default_parse_rules(), &default_params);
+ return TRUE;
+ }
+
+ LLUICtrlFactory::instance().pushFileName(xml_filename);
+
+ LL_RECORD_BLOCK_TIME(FTM_EXTERNAL_FLOATER_LOAD);
+ if (!LLUICtrlFactory::getLayeredXMLNode(xml_filename, referenced_xml))
+ {
+ LL_WARNS() << "Couldn't parse panel from: " << xml_filename << LL_ENDL;
+
+ return FALSE;
+ }
+
+ Params referenced_params;
+ parser.readXUI(referenced_xml, referenced_params, LLUICtrlFactory::getInstance()->getCurFileName());
+ params.fillFrom(referenced_params);
+
+ // add children using dimensions from referenced xml for consistent layout
+ setShape(params.rect);
+ LLUICtrlFactory::createChildren(this, referenced_xml, child_registry_t::instance());
+
+ LLUICtrlFactory::instance().popFileName();
+ }
+
+
if (output_node)
{
Params output_params(params);
setupParamsForExport(output_params, parent);
- Params default_params(LLUICtrlFactory::getDefaultParams<LLFloater>());
output_node->setName(node->getName()->mString);
- parser.writeXUI(output_node, output_params, &default_params);
+ parser.writeXUI(output_node, output_params, LLInitParam::default_parse_rules(), &default_params);
}
// Default floater position to top-left corner of screen
@@ -2903,9 +3246,8 @@ bool LLFloater::initFloaterXML(LLXMLNodePtr node, LLView *parent, const std::str
{
params.rect.left.set(0);
}
-
params.from_xui = true;
- applyXUILayout(params, parent);
+ applyXUILayout(params, parent, parent == gFloaterView ? gFloaterView->getSnapRect() : parent->getLocalRect());
initFromParams(params);
initFloater(params);
@@ -2942,14 +3284,14 @@ bool LLFloater::initFloaterXML(LLXMLNodePtr node, LLView *parent, const std::str
BOOL result;
{
- LLFastTimer ft(POST_BUILD);
+ LL_RECORD_BLOCK_TIME(POST_BUILD);
result = postBuild();
}
if (!result)
{
- llerrs << "Failed to construct floater " << getName() << llendl;
+ LL_ERRS() << "Failed to construct floater " << getName() << LL_ENDL;
}
applyRectControl(); // If we have a saved rect control, apply it
@@ -2967,6 +3309,11 @@ bool LLFloater::isShown() const
return ! isMinimized() && isInVisibleChain();
}
+bool LLFloater::isDetachedAndNotMinimized()
+{
+ return !getHost() && !isMinimized();
+}
+
/* static */
bool LLFloater::isShown(const LLFloater* floater)
{
@@ -2985,39 +3332,29 @@ bool LLFloater::isVisible(const LLFloater* floater)
return floater && floater->getVisible();
}
-static LLFastTimer::DeclareTimer FTM_BUILD_FLOATERS("Build Floaters");
+static LLTrace::BlockTimerStatHandle FTM_BUILD_FLOATERS("Build Floaters");
-bool LLFloater::buildFromFile(const std::string& filename, LLXMLNodePtr output_node)
+bool LLFloater::buildFromFile(const std::string& filename)
{
- LLFastTimer timer(FTM_BUILD_FLOATERS);
+ LL_RECORD_BLOCK_TIME(FTM_BUILD_FLOATERS);
LLXMLNodePtr root;
- //if exporting, only load the language being exported,
- //instead of layering localized version on top of english
- if (output_node)
+ if (!LLUICtrlFactory::getLayeredXMLNode(filename, root))
{
- if (!LLUICtrlFactory::getLocalizedXMLNode(filename, root))
- {
- llwarns << "Couldn't parse floater from: " << LLUI::getLocalizedSkinPath() + gDirUtilp->getDirDelimiter() + filename << llendl;
- return false;
- }
- }
- else if (!LLUICtrlFactory::getLayeredXMLNode(filename, root))
- {
- llwarns << "Couldn't parse floater from: " << LLUI::getSkinPath() + gDirUtilp->getDirDelimiter() + filename << llendl;
+ LL_WARNS() << "Couldn't find (or parse) floater from: " << filename << LL_ENDL;
return false;
}
// root must be called floater
if( !(root->hasName("floater") || root->hasName("multi_floater")) )
{
- llwarns << "Root node should be named floater in: " << filename << llendl;
+ LL_WARNS() << "Root node should be named floater in: " << filename << LL_ENDL;
return false;
}
bool res = true;
- lldebugs << "Building floater " << filename << llendl;
+ LL_DEBUGS() << "Building floater " << filename << LL_ENDL;
LLUICtrlFactory::instance().pushFileName(filename);
{
if (!getFactoryMap().empty())
@@ -3029,7 +3366,7 @@ bool LLFloater::buildFromFile(const std::string& filename, LLXMLNodePtr output_n
getCommitCallbackRegistrar().pushScope();
getEnableCallbackRegistrar().pushScope();
- res = initFloaterXML(root, getParent(), filename, output_node);
+ res = initFloaterXML(root, getParent(), filename, NULL);
setXMLFilename(filename);
@@ -3045,3 +3382,159 @@ bool LLFloater::buildFromFile(const std::string& filename, LLXMLNodePtr output_n
return res;
}
+
+void LLFloater::stackWith(LLFloater& other)
+{
+ static LLUICachedControl<S32> floater_offset ("UIFloaterOffset", 16);
+
+ LLRect next_rect;
+ if (other.getHost())
+ {
+ next_rect = other.getHost()->getRect();
+ }
+ else
+ {
+ next_rect = other.getRect();
+ }
+ next_rect.translate(floater_offset, -floater_offset);
+
+ next_rect.setLeftTopAndSize(next_rect.mLeft, next_rect.mTop, getRect().getWidth(), getRect().getHeight());
+
+ setShape(next_rect);
+
+ if (!other.getHost())
+ {
+ other.mPositioning = LLFloaterEnums::POSITIONING_CASCADE_GROUP;
+ other.setFollows(FOLLOWS_LEFT | FOLLOWS_TOP);
+ }
+}
+
+void LLFloater::applyRelativePosition()
+{
+ LLRect snap_rect = gFloaterView->getSnapRect();
+ LLRect floater_view_screen_rect = gFloaterView->calcScreenRect();
+ snap_rect.translate(floater_view_screen_rect.mLeft, floater_view_screen_rect.mBottom);
+ LLRect floater_screen_rect = calcScreenRect();
+
+ LLCoordGL new_center = mPosition.convert();
+ LLCoordGL cur_center(floater_screen_rect.getCenterX(), floater_screen_rect.getCenterY());
+ translate(new_center.mX - cur_center.mX, new_center.mY - cur_center.mY);
+}
+
+
+LLCoordFloater::LLCoordFloater(F32 x, F32 y, LLFloater& floater)
+: coord_t((S32)x, (S32)y)
+{
+ mFloater = floater.getHandle();
+}
+
+
+LLCoordFloater::LLCoordFloater(const LLCoordCommon& other, LLFloater& floater)
+{
+ mFloater = floater.getHandle();
+ convertFromCommon(other);
+}
+
+LLCoordFloater& LLCoordFloater::operator=(const LLCoordFloater& other)
+{
+ mFloater = other.mFloater;
+ coord_t::operator =(other);
+ return *this;
+}
+
+void LLCoordFloater::setFloater(LLFloater& floater)
+{
+ mFloater = floater.getHandle();
+}
+
+bool LLCoordFloater::operator==(const LLCoordFloater& other) const
+{
+ return mX == other.mX && mY == other.mY && mFloater == other.mFloater;
+}
+
+LLCoordCommon LL_COORD_FLOATER::convertToCommon() const
+{
+ const LLCoordFloater& self = static_cast<const LLCoordFloater&>(LLCoordFloater::getTypedCoords(*this));
+
+ LLRect snap_rect = gFloaterView->getSnapRect();
+ LLRect floater_view_screen_rect = gFloaterView->calcScreenRect();
+ snap_rect.translate(floater_view_screen_rect.mLeft, floater_view_screen_rect.mBottom);
+
+ LLFloater* floaterp = mFloater.get();
+ S32 floater_width = floaterp ? floaterp->getRect().getWidth() : 0;
+ S32 floater_height = floaterp ? floaterp->getRect().getHeight() : 0;
+ LLCoordCommon out;
+ if (self.mX < -0.5f)
+ {
+ out.mX = ll_round(rescale(self.mX, -1.f, -0.5f, snap_rect.mLeft - (floater_width - FLOATER_MIN_VISIBLE_PIXELS), snap_rect.mLeft));
+ }
+ else if (self.mX > 0.5f)
+ {
+ out.mX = ll_round(rescale(self.mX, 0.5f, 1.f, snap_rect.mRight - floater_width, snap_rect.mRight - FLOATER_MIN_VISIBLE_PIXELS));
+ }
+ else
+ {
+ out.mX = ll_round(rescale(self.mX, -0.5f, 0.5f, snap_rect.mLeft, snap_rect.mRight - floater_width));
+ }
+
+ if (self.mY < -0.5f)
+ {
+ out.mY = ll_round(rescale(self.mY, -1.f, -0.5f, snap_rect.mBottom - (floater_height - FLOATER_MIN_VISIBLE_PIXELS), snap_rect.mBottom));
+ }
+ else if (self.mY > 0.5f)
+ {
+ out.mY = ll_round(rescale(self.mY, 0.5f, 1.f, snap_rect.mTop - floater_height, snap_rect.mTop - FLOATER_MIN_VISIBLE_PIXELS));
+ }
+ else
+ {
+ out.mY = ll_round(rescale(self.mY, -0.5f, 0.5f, snap_rect.mBottom, snap_rect.mTop - floater_height));
+ }
+
+ // return center point instead of lower left
+ out.mX += floater_width / 2;
+ out.mY += floater_height / 2;
+
+ return out;
+}
+
+void LL_COORD_FLOATER::convertFromCommon(const LLCoordCommon& from)
+{
+ LLCoordFloater& self = static_cast<LLCoordFloater&>(LLCoordFloater::getTypedCoords(*this));
+ LLRect snap_rect = gFloaterView->getSnapRect();
+ LLRect floater_view_screen_rect = gFloaterView->calcScreenRect();
+ snap_rect.translate(floater_view_screen_rect.mLeft, floater_view_screen_rect.mBottom);
+
+
+ LLFloater* floaterp = mFloater.get();
+ S32 floater_width = floaterp ? floaterp->getRect().getWidth() : 0;
+ S32 floater_height = floaterp ? floaterp->getRect().getHeight() : 0;
+
+ S32 from_x = from.mX - floater_width / 2;
+ S32 from_y = from.mY - floater_height / 2;
+
+ if (from_x < snap_rect.mLeft)
+ {
+ self.mX = rescale(from_x, snap_rect.mLeft - (floater_width - FLOATER_MIN_VISIBLE_PIXELS), snap_rect.mLeft, -1.f, -0.5f);
+ }
+ else if (from_x + floater_width > snap_rect.mRight)
+ {
+ self.mX = rescale(from_x, snap_rect.mRight - floater_width, snap_rect.mRight - FLOATER_MIN_VISIBLE_PIXELS, 0.5f, 1.f);
+ }
+ else
+ {
+ self.mX = rescale(from_x, snap_rect.mLeft, snap_rect.mRight - floater_width, -0.5f, 0.5f);
+ }
+
+ if (from_y < snap_rect.mBottom)
+ {
+ self.mY = rescale(from_y, snap_rect.mBottom - (floater_height - FLOATER_MIN_VISIBLE_PIXELS), snap_rect.mBottom, -1.f, -0.5f);
+ }
+ else if (from_y + floater_height > snap_rect.mTop)
+ {
+ self.mY = rescale(from_y, snap_rect.mTop - floater_height, snap_rect.mTop - FLOATER_MIN_VISIBLE_PIXELS, 0.5f, 1.f);
+ }
+ else
+ {
+ self.mY = rescale(from_y, snap_rect.mBottom, snap_rect.mTop - floater_height, -0.5f, 0.5f);
+ }
+}
diff --git a/indra/llui/llfloater.h b/indra/llui/llfloater.h
index bb96272d02..ef7c6180d2 100644..100755
--- a/indra/llui/llfloater.h
+++ b/indra/llui/llfloater.h
@@ -32,9 +32,11 @@
#define LL_FLOATER_H
#include "llpanel.h"
+#include "lltoolbar.h"
#include "lluuid.h"
//#include "llnotificationsutil.h"
#include <set>
+#include <boost/signals2.hpp>
class LLDragHandle;
class LLResizeHandle;
@@ -59,12 +61,70 @@ const BOOL CLOSE_NO = FALSE;
const BOOL ADJUST_VERTICAL_YES = TRUE;
const BOOL ADJUST_VERTICAL_NO = FALSE;
-class LLFloater : public LLPanel
+namespace LLFloaterEnums
{
-friend class LLFloaterView;
-friend class LLFloaterReg;
-friend class LLMultiFloater;
+ enum EOpenPositioning
+ {
+ POSITIONING_RELATIVE,
+ POSITIONING_CASCADING,
+ POSITIONING_CASCADE_GROUP,
+ POSITIONING_CENTERED,
+ POSITIONING_SPECIFIED,
+ POSITIONING_COUNT
+ };
+}
+
+namespace LLInitParam
+{
+ template<>
+ struct TypeValues<LLFloaterEnums::EOpenPositioning> : public TypeValuesHelper<LLFloaterEnums::EOpenPositioning>
+ {
+ static void declareValues();
+ };
+}
+
+struct LL_COORD_FLOATER
+{
+ typedef F32 value_t;
+
+ LLCoordCommon convertToCommon() const;
+ void convertFromCommon(const LLCoordCommon& from);
+protected:
+ LLHandle<LLFloater> mFloater;
+};
+
+struct LLCoordFloater : LLCoord<LL_COORD_FLOATER>
+{
+ typedef LLCoord<LL_COORD_FLOATER> coord_t;
+
+ LLCoordFloater() {}
+ LLCoordFloater(F32 x, F32 y, LLFloater& floater);
+ LLCoordFloater(const LLCoordCommon& other, LLFloater& floater);
+
+ LLCoordFloater& operator=(const LLCoordCommon& other)
+ {
+ convertFromCommon(other);
+ return *this;
+ }
+
+ LLCoordFloater& operator=(const LLCoordFloater& other);
+
+ bool operator==(const LLCoordFloater& other) const;
+ bool operator!=(const LLCoordFloater& other) const { return !(*this == other); }
+
+ void setFloater(LLFloater& floater);
+
+
+};
+
+class LLFloater : public LLPanel, public LLInstanceTracker<LLFloater>
+{
+ friend class LLFloaterView;
+ friend class LLFloaterReg;
+ friend class LLMultiFloater;
+
public:
+
struct KeyCompare
{
// static bool compare(const LLSD& a, const LLSD& b);
@@ -95,7 +155,7 @@ public:
short_title;
Optional<bool> single_instance,
- auto_tile,
+ reuse_instance,
can_resize,
can_minimize,
can_close,
@@ -105,7 +165,10 @@ public:
save_visibility,
save_dock_state,
can_dock,
- open_centered;
+ show_title;
+
+ Optional<LLFloaterEnums::EOpenPositioning> positioning;
+
Optional<S32> header_height,
legacy_header_height; // HACK see initFromXML()
@@ -125,6 +188,8 @@ public:
Optional<CommitCallbackParam> open_callback,
close_callback;
+
+ Ignored follows;
Params();
};
@@ -141,26 +206,32 @@ public:
// Don't export top/left for rect, only height/width
static void setupParamsForExport(Params& p, LLView* parent);
- bool buildFromFile(const std::string &filename, LLXMLNodePtr output_node = NULL);
+ bool buildFromFile(const std::string &filename);
boost::signals2::connection setMinimizeCallback( const commit_signal_t::slot_type& cb );
+ boost::signals2::connection setOpenCallback( const commit_signal_t::slot_type& cb );
+ boost::signals2::connection setCloseCallback( const commit_signal_t::slot_type& cb );
void initFromParams(const LLFloater::Params& p);
bool initFloaterXML(LLXMLNodePtr node, LLView *parent, const std::string& filename, LLXMLNodePtr output_node = NULL);
/*virtual*/ void handleReshape(const LLRect& new_rect, bool by_user = false);
- /*virtual*/ BOOL canSnapTo(const LLView* other_view);
+ /*virtual*/ BOOL canSnapTo(const LLView* other_view);
/*virtual*/ void setSnappedTo(const LLView* snap_view);
/*virtual*/ void setFocus( BOOL b );
/*virtual*/ void setIsChrome(BOOL is_chrome);
/*virtual*/ void setRect(const LLRect &rect);
+ void setIsSingleInstance(BOOL is_single_instance);
void initFloater(const Params& p);
void openFloater(const LLSD& key = LLSD());
// If allowed, close the floater cleanly, releasing focus.
- void closeFloater(bool app_quitting = false);
+ virtual void closeFloater(bool app_quitting = false);
+
+ // Close the floater or its host. Use when hidding or toggling a floater instance.
+ virtual void closeHostedFloater();
/*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
@@ -171,6 +242,7 @@ public:
void center();
LLMultiFloater* getHost();
+ bool isDetachedAndNotMinimized();
void applyTitle();
std::string getCurrentTitle() const;
@@ -178,7 +250,6 @@ public:
std::string getTitle() const;
void setShortTitle( const std::string& short_title );
std::string getShortTitle() const;
- void setTitleVisible(bool visible);
virtual void setMinimized(BOOL b);
void moveResizeHandlesToFront();
void addDependentFloater(LLFloater* dependent, BOOL reposition = TRUE);
@@ -195,19 +266,18 @@ public:
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();
+ virtual BOOL isFrontmost();
BOOL isDependent() { return !mDependeeHandle.isDead(); }
void setCanMinimize(BOOL can_minimize);
void setCanClose(BOOL can_close);
void setCanTearOff(BOOL can_tear_off);
virtual void setCanResize(BOOL can_resize);
void setCanDrag(BOOL can_drag);
+ bool getCanDrag();
void setHost(LLMultiFloater* host);
BOOL isResizable() const { return mResizable; }
void setResizeLimits( S32 min_width, S32 min_height );
void getResizeLimits( S32* min_width, S32* min_height ) { *min_width = mMinWidth; *min_height = mMinHeight; }
- LLRect getSavedRect() const;
- bool hasSavedRect() const;
static std::string getControlName(const std::string& name, const LLSD& key);
static LLControlGroup* getControlGroup();
@@ -220,6 +290,7 @@ public:
S32 getHeaderHeight() const { return mHeaderHeight; }
virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask);
virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask);
virtual BOOL handleMiddleMouseDown(S32 x, S32 y, MASK mask);
@@ -237,9 +308,10 @@ public:
virtual BOOL canClose() { return TRUE; }
/*virtual*/ void setVisible(BOOL visible); // do not override
- /*virtual*/ void handleVisibilityChange ( BOOL new_visibility ); // do not override
+ /*virtual*/ void onVisibilityChange ( BOOL new_visibility ); // do not override
void setFrontmost(BOOL take_focus = TRUE);
+ virtual void setVisibleAndFrontmost(BOOL take_focus=TRUE, const LLSD& key = LLSD());
// Defaults to false.
virtual BOOL canSaveAs() const { return FALSE; }
@@ -250,9 +322,9 @@ public:
void clearSnapTarget() { mSnappedTo.markDead(); }
LLHandle<LLFloater> getSnapTarget() const { return mSnappedTo; }
- LLHandle<LLFloater> getHandle() const { return mHandle; }
+ LLHandle<LLFloater> getHandle() const { return getDerivedHandle<LLFloater>(); }
const LLSD& getKey() { return mKey; }
- BOOL matchesKey(const LLSD& key) { return mSingleInstance || KeyCompare::equate(key, mKey); }
+ virtual bool matchesKey(const LLSD& key) { return mSingleInstance || KeyCompare::equate(key, mKey); }
const std::string& getInstanceName() { return mInstanceName; }
@@ -263,13 +335,13 @@ public:
virtual void setDocked(bool docked, bool pop_on_undock = true);
virtual void setTornOff(bool torn_off) { mTornOff = torn_off; }
+ bool isTornOff() {return mTornOff;}
+ void setOpenPositioning(LLFloaterEnums::EOpenPositioning pos) {mPositioning = pos;}
- // Return a closeable floater, if any, given the current focus.
- static LLFloater* getClosableFloaterFromFocus();
- // Close the floater returned by getClosableFloaterFromFocus() and
+ // Close the floater returned by getFrontmostClosableFloater() and
// handle refocusing.
- static void closeFocusedFloater();
+ static void closeFrontmostFloater();
// LLNotification::Params contextualNotification(const std::string& name)
// {
@@ -287,14 +359,20 @@ public:
void updateTransparency(ETypeTransparency transparency_type);
+ void enableResizeCtrls(bool enable, bool width = true, bool height = true);
+
+ bool isPositioning(LLFloaterEnums::EOpenPositioning p) const { return (p == mPositioning); }
protected:
+ void applyControlsAndPosition(LLFloater* other);
- void setRectControl(const std::string& rectname) { mRectControl = rectname; };
+ void stackWith(LLFloater& other);
- virtual void applySavedVariables();
+ virtual void initRectControl();
+ virtual bool applyRectControl();
+ bool applyDockState();
+ void applyPositioning(LLFloater* other, bool on_open);
+ void applyRelativePosition();
- void applyRectControl();
- void applyDockState();
void storeRectControl();
void storeVisibilityControl();
void storeDockStateControl();
@@ -303,7 +381,6 @@ protected:
void setInstanceName(const std::string& name);
virtual void bringToFront(S32 x, S32 y);
- virtual void setVisibleAndFrontmost(BOOL take_focus=TRUE);
void setExpandedRect(const LLRect& rect) { mExpandedRect = rect; } // size when not minimized
const LLRect& getExpandedRect() const { return mExpandedRect; }
@@ -314,7 +391,7 @@ protected:
void destroy(); // Don't call this directly. You probably want to call closeFloater()
- virtual void onClickCloseBtn();
+ virtual void onClickCloseBtn(bool app_quitting = false);
virtual void updateTitleButtons();
@@ -339,7 +416,6 @@ private:
BOOL offerClickToButton(S32 x, S32 y, MASK mask, EFloaterButton index);
void addResizeCtrls();
void layoutResizeCtrls();
- void enableResizeCtrls(bool enable);
void addDragHandle();
void layoutDragHandle(); // repair layout
@@ -359,7 +435,10 @@ public:
commit_signal_t* mMinimizeSignal;
protected:
+ bool mSaveRect;
std::string mRectControl;
+ std::string mPosXControl;
+ std::string mPosYControl;
std::string mVisibilityControl;
std::string mDocStateControl;
LLSD mKey; // Key used for retrieving instances; set (for now) by LLFLoaterReg
@@ -375,16 +454,19 @@ private:
LLUIString mTitle;
LLUIString mShortTitle;
- BOOL mSingleInstance; // TRUE if there is only ever one instance of the floater
- std::string mInstanceName; // Store the instance name so we can remove ourselves from the list
- BOOL mAutoTile; // TRUE if placement of new instances tiles
+ BOOL mSingleInstance; // TRUE if there is only ever one instance of the floater
+ bool mReuseInstance; // true if we want to hide the floater when we close it instead of destroying it
+ bool mIsReuseInitialized; // true if mReuseInstance already set from parameters
+ std::string mInstanceName; // Store the instance name so we can remove ourselves from the list
BOOL mCanTearOff;
BOOL mCanMinimize;
BOOL mCanClose;
BOOL mDragOnLeft;
BOOL mResizable;
- bool mOpenCentered;
+
+ LLFloaterEnums::EOpenPositioning mPositioning;
+ LLCoordFloater mPosition;
S32 mMinWidth;
S32 mMinHeight;
@@ -423,18 +505,9 @@ private:
typedef void(*click_callback)(LLFloater*);
static click_callback sButtonCallbacks[BUTTON_COUNT];
- typedef std::map<LLHandle<LLFloater>, LLFloater*> handle_map_t;
- typedef std::map<LLHandle<LLFloater>, LLFloater*>::iterator handle_map_iter_t;
- static handle_map_t sFloaterMap;
-
- std::vector<LLHandle<LLView> > mMinimizedHiddenChildren;
-
BOOL mHasBeenDraggedWhileMinimized;
S32 mPreviousMinimizedBottom;
S32 mPreviousMinimizedLeft;
-
-// LLFloaterNotificationContext* mNotificationContext;
- LLRootHandle<LLFloater> mHandle;
};
@@ -442,6 +515,8 @@ private:
// LLFloaterView
// Parent of all floating panels
+const S32 FLOATER_MIN_VISIBLE_PIXELS = 16;
+
class LLFloaterView : public LLUICtrl
{
public:
@@ -454,8 +529,6 @@ protected:
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);
-
/*virtual*/ void draw();
/*virtual*/ LLRect getSnapRect() const;
/*virtual*/ void refresh();
@@ -463,7 +536,7 @@ public:
LLRect findNeighboringPosition( LLFloater* reference_floater, LLFloater* neighbor );
// Given a child of gFloaterView, make sure this view can fit entirely onscreen.
- void adjustToFitScreen(LLFloater* floater, BOOL allow_partial_outside);
+ void adjustToFitScreen(LLFloater* floater, BOOL allow_partial_outside, BOOL snap_in_toolbars = false);
void setMinimizePositionVerticalOffset(S32 offset) { mMinimizePositionVOffset = offset; }
void getMinimizePosition( S32 *left, S32 *bottom);
@@ -484,6 +557,10 @@ public:
BOOL allChildrenClosed();
void shiftFloaters(S32 x_offset, S32 y_offset);
+ void hideAllFloaters();
+ void showHiddenFloaters();
+
+
LLFloater* getFrontmost() const;
LLFloater* getBackmost() const;
LLFloater* getParentFloater(LLView* viewp) const;
@@ -495,14 +572,26 @@ public:
// value is not defined.
S32 getZOrder(LLFloater* child);
- void setSnapOffsetBottom(S32 offset) { mSnapOffsetBottom = offset; }
- void setSnapOffsetRight(S32 offset) { mSnapOffsetRight = offset; }
+ void setFloaterSnapView(LLHandle<LLView> snap_view) {mSnapView = snap_view; }
+ LLFloater* getFrontmostClosableFloater();
+
+ void setToolbarRect(LLToolBarEnums::EToolBarLocation tb, const LLRect& toolbar_rect);
private:
+ void hiddenFloaterClosed(LLFloater* floater);
+
+ LLRect mLastSnapRect;
+ LLRect mToolbarLeftRect;
+ LLRect mToolbarBottomRect;
+ LLRect mToolbarRightRect;
+ LLHandle<LLView> mSnapView;
BOOL mFocusCycleMode;
S32 mSnapOffsetBottom;
S32 mSnapOffsetRight;
S32 mMinimizePositionVOffset;
+ typedef std::vector<std::pair<LLHandle<LLFloater>, boost::signals2::connection> > hidden_floaters_t;
+ hidden_floaters_t mHiddenFloaters;
+ LLFloater * mFrontChild;
};
//
diff --git a/indra/llui/llfloaterreg.cpp b/indra/llui/llfloaterreg.cpp
index 4677d535db..9ef290abc0 100644..100755
--- a/indra/llui/llfloaterreg.cpp
+++ b/indra/llui/llfloaterreg.cpp
@@ -57,29 +57,59 @@ void LLFloaterReg::add(const std::string& name, const std::string& filename, con
}
//static
-LLRect LLFloaterReg::getFloaterRect(const std::string& name)
+LLFloater* LLFloaterReg::getLastFloaterInGroup(const std::string& name)
{
- LLRect rect;
const std::string& groupname = sGroupMap[name];
if (!groupname.empty())
{
instance_list_t& list = sInstanceMap[groupname];
if (!list.empty())
{
- static LLUICachedControl<S32> floater_offset ("UIFloaterOffset", 16);
- LLFloater* last_floater = list.back();
- if (last_floater->getHost())
+ for (instance_list_t::reverse_iterator iter = list.rbegin(); iter != list.rend(); ++iter)
{
- rect = last_floater->getHost()->getRect();
+ LLFloater* inst = *iter;
+
+ if (inst->getVisible() && !inst->isMinimized())
+ {
+ return inst;
+ }
}
- else
+ }
+ }
+ return NULL;
+}
+
+LLFloater* LLFloaterReg::getLastFloaterCascading()
+{
+ LLRect candidate_rect;
+ candidate_rect.mTop = 100000;
+ LLFloater* candidate_floater = NULL;
+
+ std::map<std::string,std::string>::const_iterator it = sGroupMap.begin(), it_end = sGroupMap.end();
+ for( ; it != it_end; ++it)
+ {
+ const std::string& group_name = it->second;
+
+ instance_list_t& instances = sInstanceMap[group_name];
+
+ for (instance_list_t::const_iterator iter = instances.begin(); iter != instances.end(); ++iter)
+ {
+ LLFloater* inst = *iter;
+
+ if (inst->getVisible()
+ && (inst->isPositioning(LLFloaterEnums::POSITIONING_CASCADING)
+ || inst->isPositioning(LLFloaterEnums::POSITIONING_CASCADE_GROUP)))
{
- rect = last_floater->getRect();
+ if (candidate_rect.mTop > inst->getRect().mTop)
+ {
+ candidate_floater = inst;
+ candidate_rect = inst->getRect();
+ }
}
- rect.translate(floater_offset, -floater_offset);
}
}
- return rect;
+
+ return candidate_floater;
}
//static
@@ -117,40 +147,39 @@ LLFloater* LLFloaterReg::getInstance(const std::string& name, const LLSD& key)
if (!groupname.empty())
{
instance_list_t& list = sInstanceMap[groupname];
- int index = list.size();
res = build_func(key);
-
- bool success = res->buildFromFile(xui_file, NULL);
- if (!success)
+ if (!res)
{
- llwarns << "Failed to build floater type: '" << name << "'." << llendl;
+ LL_WARNS() << "Failed to build floater type: '" << name << "'." << LL_ENDL;
return NULL;
}
-
- // Note: key should eventually be a non optional LLFloater arg; for now, set mKey to be safe
- res->mKey = key;
- res->setInstanceName(name);
- res->applySavedVariables(); // Can't apply rect and dock state until setting instance name
- if (res->mAutoTile && !res->getHost() && index > 0)
+ bool success = res->buildFromFile(xui_file);
+ if (!success)
{
- const LLRect& cur_rect = res->getRect();
- LLRect next_rect = getFloaterRect(groupname);
- next_rect.setLeftTopAndSize(next_rect.mLeft, next_rect.mTop, cur_rect.getWidth(), cur_rect.getHeight());
- res->setRect(next_rect);
- res->setRectControl(LLStringUtil::null); // don't save rect of tiled floaters
- gFloaterView->adjustToFitScreen(res, true);
+ LL_WARNS() << "Failed to build floater type: '" << name << "'." << LL_ENDL;
+ return NULL;
}
- else
+
+ // Note: key should eventually be a non optional LLFloater arg; for now, set mKey to be safe
+ if (res->mKey.isUndefined())
{
- gFloaterView->adjustToFitScreen(res, false);
+ res->mKey = key;
}
+ res->setInstanceName(name);
+
+ LLFloater *last_floater = (list.empty() ? NULL : list.back());
+
+ res->applyControlsAndPosition(last_floater);
+
+ gFloaterView->adjustToFitScreen(res, false);
+
list.push_back(res);
}
}
if (!res)
{
- llwarns << "Floater type: '" << name << "' not registered." << llendl;
+ LL_WARNS() << "Floater type: '" << name << "' not registered." << LL_ENDL;
}
}
return res;
@@ -235,17 +264,9 @@ bool LLFloaterReg::hideInstance(const std::string& name, const LLSD& key)
LLFloater* instance = findInstance(name, key);
if (instance)
{
- // When toggling *visibility*, close the host instead of the floater when hosted
- if (instance->getHost())
- instance->getHost()->closeFloater();
- else
- instance->closeFloater();
- return true;
- }
- else
- {
- return false;
+ instance->closeHostedFloater();
}
+ return (instance != NULL);
}
//static
@@ -255,11 +276,7 @@ bool LLFloaterReg::toggleInstance(const std::string& name, const LLSD& key)
LLFloater* instance = findInstance(name, key);
if (LLFloater::isShown(instance))
{
- // When toggling *visibility*, close the host instead of the floater when hosted
- if (instance->getHost())
- instance->getHost()->closeFloater();
- else
- instance->closeFloater();
+ instance->closeHostedFloater();
return false;
}
else
@@ -331,9 +348,7 @@ void LLFloaterReg::restoreVisibleInstances()
//static
std::string LLFloaterReg::getRectControlName(const std::string& name)
{
- std::string res = std::string("floater_rect_") + name;
- LLStringUtil::replaceChar( res, ' ', '_' );
- return res;
+ return std::string("floater_rect_") + getBaseControlName(name);
}
//static
@@ -341,26 +356,55 @@ std::string LLFloaterReg::declareRectControl(const std::string& name)
{
std::string controlname = getRectControlName(name);
LLFloater::getControlGroup()->declareRect(controlname, LLRect(),
- llformat("Window Position and Size for %s", name.c_str()),
- TRUE);
+ llformat("Window Size for %s", name.c_str()),
+ LLControlVariable::PERSIST_NONDFT);
+ return controlname;
+}
+
+std::string LLFloaterReg::declarePosXControl(const std::string& name)
+{
+ std::string controlname = std::string("floater_pos_") + getBaseControlName(name) + "_x";
+ LLFloater::getControlGroup()->declareF32(controlname,
+ 10.f,
+ llformat("Window X Position for %s", name.c_str()),
+ LLControlVariable::PERSIST_NONDFT);
return controlname;
}
+std::string LLFloaterReg::declarePosYControl(const std::string& name)
+{
+ std::string controlname = std::string("floater_pos_") + getBaseControlName(name) + "_y";
+ LLFloater::getControlGroup()->declareF32(controlname,
+ 10.f,
+ llformat("Window Y Position for %s", name.c_str()),
+ LLControlVariable::PERSIST_NONDFT);
+
+ return controlname;
+}
+
+
//static
std::string LLFloaterReg::getVisibilityControlName(const std::string& name)
{
- std::string res = std::string("floater_vis_") + name;
+ return std::string("floater_vis_") + getBaseControlName(name);
+}
+
+//static
+std::string LLFloaterReg::getBaseControlName(const std::string& name)
+{
+ std::string res(name);
LLStringUtil::replaceChar( res, ' ', '_' );
return res;
}
+
//static
std::string LLFloaterReg::declareVisibilityControl(const std::string& name)
{
std::string controlname = getVisibilityControlName(name);
LLFloater::getControlGroup()->declareBOOL(controlname, FALSE,
llformat("Window Visibility for %s", name.c_str()),
- TRUE);
+ LLControlVariable::PERSIST_NONDFT);
return controlname;
}
@@ -370,7 +414,7 @@ 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);
+ LLControlVariable::PERSIST_NONDFT);
return controlname;
}
@@ -410,70 +454,98 @@ void LLFloaterReg::registerControlVariables()
}
}
-// Callbacks
-
-// static
-// Call once (i.e use for init callbacks)
-void LLFloaterReg::initUICtrlToFloaterVisibilityControl(LLUICtrl* ctrl, const LLSD& sdname)
+//static
+void LLFloaterReg::toggleInstanceOrBringToFront(const LLSD& sdname, const LLSD& key)
{
- // 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(LLFloater::getControlGroup()->getControl(vis_control_name));
-}
+ //
+ // Floaters controlled by the toolbar behave a bit differently from others.
+ // Namely they have 3-4 states as defined in the design wiki page here:
+ // https://wiki.lindenlab.com/wiki/FUI_Button_states
+ //
+ // The basic idea is this:
+ // * If the target floater is minimized, this button press will un-minimize it.
+ // * Else if the target floater is closed open it.
+ // * Else if the target floater does not have focus, give it focus.
+ // * Also, if it is not on top, bring it forward when focus is given.
+ // * Else the target floater is open, close it.
+ //
+ std::string name = sdname.asString();
+ LLFloater* instance = getInstance(name, key);
+
-// callback args may use "floatername.key" format
-static void parse_name_key(std::string& name, LLSD& key)
-{
- std::string instname = name;
- std::size_t dotpos = instname.find(".");
- if (dotpos != std::string::npos)
+ if (!instance)
+ {
+ LL_DEBUGS() << "Unable to get instance of floater '" << name << "'" << LL_ENDL;
+ return;
+ }
+
+ // If hosted, we need to take that into account
+ LLFloater* host = instance->getHost();
+
+ if (host)
+ {
+ if (host->isMinimized() || !host->isShown() || !host->isFrontmost())
+ {
+ host->setMinimized(FALSE);
+ instance->openFloater(key);
+ instance->setVisibleAndFrontmost(true, key);
+ }
+ else if (!instance->getVisible())
+ {
+ instance->openFloater(key);
+ instance->setVisibleAndFrontmost(true, key);
+ instance->setFocus(TRUE);
+ }
+ else
+ {
+ instance->closeHostedFloater();
+ }
+ }
+ else
{
- name = instname.substr(0, dotpos);
- key = LLSD(instname.substr(dotpos+1, std::string::npos));
+ if (instance->isMinimized())
+ {
+ instance->setMinimized(FALSE);
+ instance->setVisibleAndFrontmost(true, key);
+ }
+ else if (!instance->isShown())
+ {
+ instance->openFloater(key);
+ instance->setVisibleAndFrontmost(true, key);
+ }
+ else if (!instance->isFrontmost())
+ {
+ instance->setVisibleAndFrontmost(true, key);
+ }
+ else
+ {
+ instance->closeHostedFloater();
+ }
}
}
-//static
-void LLFloaterReg::showFloaterInstance(const LLSD& sdname)
-{
- LLSD key;
- std::string name = sdname.asString();
- parse_name_key(name, key);
- showInstance(name, key, TRUE);
-}
-//static
-void LLFloaterReg::hideFloaterInstance(const LLSD& sdname)
-{
- LLSD key;
- std::string name = sdname.asString();
- parse_name_key(name, key);
- hideInstance(name, key);
-}
-//static
-void LLFloaterReg::toggleFloaterInstance(const LLSD& sdname)
+// static
+U32 LLFloaterReg::getVisibleFloaterInstanceCount()
{
- LLSD key;
- std::string name = sdname.asString();
- parse_name_key(name, key);
- toggleInstance(name, key);
-}
+ U32 count = 0;
-//static
-bool LLFloaterReg::floaterInstanceVisible(const LLSD& sdname)
-{
- LLSD key;
- std::string name = sdname.asString();
- parse_name_key(name, key);
- return instanceVisible(name, key);
-}
+ std::map<std::string,std::string>::const_iterator it = sGroupMap.begin(), it_end = sGroupMap.end();
+ for( ; it != it_end; ++it)
+ {
+ const std::string& group_name = it->second;
-//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);
+ instance_list_t& instances = sInstanceMap[group_name];
+
+ for (instance_list_t::const_iterator iter = instances.begin(); iter != instances.end(); ++iter)
+ {
+ LLFloater* inst = *iter;
+
+ if (inst->getVisible() && !inst->isMinimized())
+ {
+ count++;
+ }
+ }
+ }
+
+ return count;
}
diff --git a/indra/llui/llfloaterreg.h b/indra/llui/llfloaterreg.h
index 94387fb41a..e3b17dcb4f 100644..100755
--- a/indra/llui/llfloaterreg.h
+++ b/indra/llui/llfloaterreg.h
@@ -27,14 +27,10 @@
#define LLFLOATERREG_H
/// llcommon
-#include "llboost.h"
#include "llrect.h"
-#include "llstl.h"
#include "llsd.h"
-/// llui
-#include "lluictrl.h"
-
+#include <list>
#include <boost/function.hpp>
//*******************************************************
@@ -43,6 +39,7 @@
//
class LLFloater;
+class LLUICtrl;
typedef boost::function<LLFloater* (const LLSD& key)> LLFloaterBuildFunc;
@@ -90,7 +87,8 @@ public:
const std::string& groupname = LLStringUtil::null);
// Helpers
- static LLRect getFloaterRect(const std::string& name);
+ static LLFloater* getLastFloaterInGroup(const std::string& name);
+ static LLFloater* getLastFloaterCascading();
// Find / get (create) / remove / destroy
static LLFloater* findInstance(const std::string& name, const LLSD& key = LLSD());
@@ -118,21 +116,18 @@ public:
// Control Variables
static std::string getRectControlName(const std::string& name);
static std::string declareRectControl(const std::string& name);
+ static std::string declarePosXControl(const std::string& name);
+ static std::string declarePosYControl(const std::string& name);
static std::string getVisibilityControlName(const std::string& name);
static std::string declareVisibilityControl(const std::string& name);
-
+ static std::string getBaseControlName(const std::string& name);
static std::string declareDockStateControl(const std::string& name);
static std::string getDockStateControlName(const std::string& name);
static void registerControlVariables();
// Callback wrappers
- static void 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);
+ static void toggleInstanceOrBringToFront(const LLSD& sdname, const LLSD& key = LLSD());
// Typed find / get / show
template <class T>
@@ -155,6 +150,7 @@ public:
static void blockShowFloaters(bool value) { sBlockShowFloaters = value;}
+ static U32 getVisibleFloaterInstanceCount();
};
#endif
diff --git a/indra/llui/llfloaterreglistener.cpp b/indra/llui/llfloaterreglistener.cpp
index 821d4543ae..7525b8cab3 100644..100755
--- a/indra/llui/llfloaterreglistener.cpp
+++ b/indra/llui/llfloaterreglistener.cpp
@@ -60,6 +60,11 @@ LLFloaterRegListener::LLFloaterRegListener():
"Ask to toggle the state of the floater specified in [\"name\"]",
&LLFloaterRegListener::toggleInstance,
requiredName);
+ add("instanceVisible",
+ "Return on [\"reply\"] an event whose [\"visible\"] indicates the visibility "
+ "of the floater specified in [\"name\"]",
+ &LLFloaterRegListener::instanceVisible,
+ requiredName);
LLSD requiredNameButton;
requiredNameButton["name"] = LLSD();
requiredNameButton["button"] = LLSD();
@@ -71,9 +76,7 @@ LLFloaterRegListener::LLFloaterRegListener():
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());
+ LLSD reply;
// 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
@@ -86,7 +89,7 @@ void LLFloaterRegListener::getBuildMap(const LLSD& event) const
reply[mi->first] = mi->second.mFile;
}
// Send the reply to the LLEventPump named in event["reply"].
- LLEventPumps::instance().obtain(event["reply"]).post(reply);
+ sendReply(reply, event);
}
void LLFloaterRegListener::showInstance(const LLSD& event) const
@@ -104,6 +107,12 @@ void LLFloaterRegListener::toggleInstance(const LLSD& event) const
LLFloaterReg::toggleInstance(event["name"], event["key"]);
}
+void LLFloaterRegListener::instanceVisible(const LLSD& event) const
+{
+ sendReply(LLSDMap("visible", LLFloaterReg::instanceVisible(event["name"], event["key"])),
+ event);
+}
+
void LLFloaterRegListener::clickButton(const LLSD& event) const
{
// If the caller requests a reply, build the reply.
diff --git a/indra/llui/llfloaterreglistener.h b/indra/llui/llfloaterreglistener.h
index 586656667c..24311a2dfa 100644..100755
--- a/indra/llui/llfloaterreglistener.h
+++ b/indra/llui/llfloaterreglistener.h
@@ -47,6 +47,7 @@ private:
void showInstance(const LLSD& event) const;
void hideInstance(const LLSD& event) const;
void toggleInstance(const LLSD& event) const;
+ void instanceVisible(const LLSD& event) const;
void clickButton(const LLSD& event) const;
};
diff --git a/indra/llui/llflyoutbutton.cpp b/indra/llui/llflyoutbutton.cpp
index 4b3a0a5d21..4b3a0a5d21 100644..100755
--- a/indra/llui/llflyoutbutton.cpp
+++ b/indra/llui/llflyoutbutton.cpp
diff --git a/indra/llui/llflyoutbutton.h b/indra/llui/llflyoutbutton.h
index 8d59380a00..36998eba2e 100644..100755
--- a/indra/llui/llflyoutbutton.h
+++ b/indra/llui/llflyoutbutton.h
@@ -46,7 +46,7 @@ public:
: action_button("action_button"),
allow_text_entry("allow_text_entry")
{
- LLComboBox::Params::allow_text_entry = false;
+ changeDefault(LLComboBox::Params::allow_text_entry, false);
}
};
diff --git a/indra/llui/llfocusmgr.cpp b/indra/llui/llfocusmgr.cpp
index 43e5f6b051..547f0bd398 100644..100755
--- a/indra/llui/llfocusmgr.cpp
+++ b/indra/llui/llfocusmgr.cpp
@@ -113,6 +113,16 @@ boost::signals2::connection LLFocusableElement::setTopLostCallback(const focus_s
+typedef std::list<LLHandle<LLView> > view_handle_list_t;
+typedef std::map<LLHandle<LLView>, LLHandle<LLView> > focus_history_map_t;
+struct LLFocusMgr::Impl
+{
+ // caching list of keyboard focus ancestors for calling onFocusReceived and onFocusLost
+ view_handle_list_t mCachedKeyboardFocusList;
+
+ focus_history_map_t mFocusHistory;
+};
+
LLFocusMgr gFocusMgr;
LLFocusMgr::LLFocusMgr()
@@ -123,10 +133,17 @@ LLFocusMgr::LLFocusMgr()
mDefaultKeyboardFocus( NULL ),
mKeystrokesOnly(FALSE),
mTopCtrl( NULL ),
- mAppHasFocus(TRUE) // Macs don't seem to notify us that we've gotten focus, so default to true
+ mAppHasFocus(TRUE), // Macs don't seem to notify us that we've gotten focus, so default to true
+ mImpl(new LLFocusMgr::Impl)
{
}
+LLFocusMgr::~LLFocusMgr()
+{
+ mImpl->mFocusHistory.clear();
+ delete mImpl;
+ mImpl = NULL;
+}
void LLFocusMgr::releaseFocusIfNeeded( LLView* view )
{
@@ -179,7 +196,7 @@ void LLFocusMgr::setKeyboardFocus(LLFocusableElement* new_focus, BOOL lock, BOOL
mKeyboardFocus = new_focus;
// list of the focus and it's ancestors
- view_handle_list_t old_focus_list = mCachedKeyboardFocusList;
+ view_handle_list_t old_focus_list = mImpl->mCachedKeyboardFocusList;
view_handle_list_t new_focus_list;
// walk up the tree to root and add all views to the new_focus_list
@@ -206,7 +223,7 @@ void LLFocusMgr::setKeyboardFocus(LLFocusableElement* new_focus, BOOL lock, BOOL
LLView* old_focus_view = old_focus_iter->get();
if (old_focus_view)
{
- mCachedKeyboardFocusList.pop_front();
+ mImpl->mCachedKeyboardFocusList.pop_front();
old_focus_view->onFocusLost();
}
}
@@ -219,7 +236,7 @@ void LLFocusMgr::setKeyboardFocus(LLFocusableElement* new_focus, BOOL lock, BOOL
LLView* new_focus_view = new_focus_riter->get();
if (new_focus_view)
{
- mCachedKeyboardFocusList.push_front(new_focus_view->getHandle());
+ mImpl->mCachedKeyboardFocusList.push_front(new_focus_view->getHandle());
new_focus_view->onFocusReceived();
}
}
@@ -254,7 +271,7 @@ void LLFocusMgr::setKeyboardFocus(LLFocusableElement* new_focus, BOOL lock, BOOL
if (focus_subtree)
{
LLView* focused_view = dynamic_cast<LLView*>(mKeyboardFocus);
- mFocusHistory[focus_subtree->getHandle()] = focused_view ? focused_view->getHandle() : LLHandle<LLView>();
+ mImpl->mFocusHistory[focus_subtree->getHandle()] = focused_view ? focused_view->getHandle() : LLHandle<LLView>();
}
}
@@ -341,11 +358,11 @@ void LLFocusMgr::setMouseCapture( LLMouseHandler* new_captor )
{
if (new_captor)
{
- llinfos << "New mouse captor: " << new_captor->getName() << llendl;
+ LL_INFOS() << "New mouse captor: " << new_captor->getName() << LL_ENDL;
}
else
{
- llinfos << "New mouse captor: NULL" << llendl;
+ LL_INFOS() << "New mouse captor: NULL" << LL_ENDL;
}
}
@@ -452,15 +469,15 @@ void LLFocusMgr::setAppHasFocus(BOOL focus)
mAppHasFocus = focus;
}
-LLUICtrl* LLFocusMgr::getLastFocusForGroup(LLView* subtree_root) const
+LLView* LLFocusMgr::getLastFocusForGroup(LLView* subtree_root) const
{
if (subtree_root)
{
- focus_history_map_t::const_iterator found_it = mFocusHistory.find(subtree_root->getHandle());
- if (found_it != mFocusHistory.end())
+ focus_history_map_t::const_iterator found_it = mImpl->mFocusHistory.find(subtree_root->getHandle());
+ if (found_it != mImpl->mFocusHistory.end())
{
// found last focus for this subtree
- return static_cast<LLUICtrl*>(found_it->second.get());
+ return found_it->second.get();
}
}
return NULL;
@@ -470,6 +487,6 @@ void LLFocusMgr::clearLastFocusForGroup(LLView* subtree_root)
{
if (subtree_root)
{
- mFocusHistory.erase(subtree_root->getHandle());
+ mImpl->mFocusHistory.erase(subtree_root->getHandle());
}
}
diff --git a/indra/llui/llfocusmgr.h b/indra/llui/llfocusmgr.h
index 22c1895075..afd2a8ce06 100644..100755
--- a/indra/llui/llfocusmgr.h
+++ b/indra/llui/llfocusmgr.h
@@ -74,7 +74,7 @@ class LLFocusMgr
{
public:
LLFocusMgr();
- ~LLFocusMgr() { mFocusHistory.clear(); }
+ ~LLFocusMgr();
// Mouse Captor
void setMouseCapture(LLMouseHandler* new_captor); // new_captor = NULL to release the mouse.
@@ -92,12 +92,12 @@ public:
void setKeystrokesOnly(BOOL keystrokes_only) { mKeystrokesOnly = keystrokes_only; }
F32 getFocusFlashAmt() const;
- S32 getFocusFlashWidth() const { return llround(lerp(1.f, 3.f, getFocusFlashAmt())); }
+ S32 getFocusFlashWidth() const { return ll_round(lerp(1.f, 3.f, getFocusFlashAmt())); }
LLColor4 getFocusColor() const;
void triggerFocusFlash();
BOOL getAppHasFocus() const { return mAppHasFocus; }
void setAppHasFocus(BOOL focus);
- LLUICtrl* getLastFocusForGroup(LLView* subtree_root) const;
+ LLView* getLastFocusForGroup(LLView* subtree_root) const;
void clearLastFocusForGroup(LLView* subtree_root);
// If setKeyboardFocus(NULL) is called, and there is a non-NULL default
@@ -120,6 +120,8 @@ public:
bool keyboardFocusHasAccelerators() const;
+ struct Impl;
+
private:
LLUICtrl* mLockedView;
@@ -132,10 +134,6 @@ private:
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;
@@ -143,8 +141,7 @@ private:
BOOL mAppHasFocus;
- typedef std::map<LLHandle<LLView>, LLHandle<LLView> > focus_history_map_t;
- focus_history_map_t mFocusHistory;
+ Impl * mImpl;
};
extern LLFocusMgr gFocusMgr;
diff --git a/indra/llui/llfolderview.cpp b/indra/llui/llfolderview.cpp
new file mode 100755
index 0000000000..4c05d001a0
--- /dev/null
+++ b/indra/llui/llfolderview.cpp
@@ -0,0 +1,1975 @@
+/**
+ * @file llfolderview.cpp
+ * @brief Implementation of the folder view collection of classes.
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llfolderview.h"
+#include "llfolderviewmodel.h"
+#include "llclipboard.h" // *TODO: remove this once hack below gone.
+#include "llkeyboard.h"
+#include "lllineeditor.h"
+#include "llmenugl.h"
+#include "llpanel.h"
+#include "llscrollcontainer.h" // hack to allow scrolling
+#include "lltextbox.h"
+#include "lltrans.h"
+#include "llui.h"
+#include "lluictrlfactory.h"
+
+// Linden library includes
+#include "lldbstrings.h"
+#include "llfocusmgr.h"
+#include "llfontgl.h"
+#include "llgl.h"
+#include "llrender.h"
+
+// Third-party library includes
+#include <algorithm>
+
+///----------------------------------------------------------------------------
+/// Local function declarations, constants, enums, and typedefs
+///----------------------------------------------------------------------------
+
+const S32 RENAME_HEIGHT_PAD = 1;
+const S32 AUTO_OPEN_STACK_DEPTH = 16;
+
+const S32 MINIMUM_RENAMER_WIDTH = 80;
+
+// *TODO: move in params in xml if necessary. Requires modification of LLFolderView & LLInventoryPanel Params.
+const S32 STATUS_TEXT_HPAD = 6;
+const S32 STATUS_TEXT_VPAD = 8;
+
+enum {
+ SIGNAL_NO_KEYBOARD_FOCUS = 1,
+ SIGNAL_KEYBOARD_FOCUS = 2
+};
+
+F32 LLFolderView::sAutoOpenTime = 1.f;
+
+//---------------------------------------------------------------------------
+
+// Tells all folders in a folderview to close themselves
+// For efficiency, calls setOpenArrangeRecursively().
+// The calling function must then call:
+// LLFolderView* root = getRoot();
+// if( root )
+// {
+// root->arrange( NULL, NULL );
+// root->scrollToShowSelection();
+// }
+// to patch things up.
+class LLCloseAllFoldersFunctor : public LLFolderViewFunctor
+{
+public:
+ LLCloseAllFoldersFunctor(BOOL close) { mOpen = !close; }
+ virtual ~LLCloseAllFoldersFunctor() {}
+ virtual void doFolder(LLFolderViewFolder* folder);
+ virtual void doItem(LLFolderViewItem* item);
+
+ BOOL mOpen;
+};
+
+
+void LLCloseAllFoldersFunctor::doFolder(LLFolderViewFolder* folder)
+{
+ folder->setOpenArrangeRecursively(mOpen);
+}
+
+// Do nothing.
+void LLCloseAllFoldersFunctor::doItem(LLFolderViewItem* item)
+{ }
+
+///----------------------------------------------------------------------------
+/// Class LLFolderViewScrollContainer
+///----------------------------------------------------------------------------
+
+// virtual
+const LLRect LLFolderViewScrollContainer::getScrolledViewRect() const
+{
+ LLRect rect = LLRect::null;
+ if (mScrolledView)
+ {
+ LLFolderView* folder_view = dynamic_cast<LLFolderView*>(mScrolledView);
+ if (folder_view)
+ {
+ S32 height = folder_view->getRect().getHeight();
+
+ rect = mScrolledView->getRect();
+ rect.setLeftTopAndSize(rect.mLeft, rect.mTop, rect.getWidth(), height);
+ }
+ }
+
+ return rect;
+}
+
+LLFolderViewScrollContainer::LLFolderViewScrollContainer(const LLScrollContainer::Params& p)
+: LLScrollContainer(p)
+{}
+
+///----------------------------------------------------------------------------
+/// Class LLFolderView
+///----------------------------------------------------------------------------
+LLFolderView::Params::Params()
+: title("title"),
+ use_label_suffix("use_label_suffix"),
+ allow_multiselect("allow_multiselect", true),
+ show_empty_message("show_empty_message", true),
+ use_ellipses("use_ellipses", false),
+ options_menu("options_menu", "")
+{
+ folder_indentation = -4;
+}
+
+
+// Default constructor
+LLFolderView::LLFolderView(const Params& p)
+: LLFolderViewFolder(p),
+ mScrollContainer( NULL ),
+ mPopupMenuHandle(),
+ mAllowMultiSelect(p.allow_multiselect),
+ mShowEmptyMessage(p.show_empty_message),
+ mShowFolderHierarchy(FALSE),
+ mRenameItem( NULL ),
+ mNeedsScroll( FALSE ),
+ mUseLabelSuffix(p.use_label_suffix),
+ mPinningSelectedItem(FALSE),
+ mNeedsAutoSelect( FALSE ),
+ mAutoSelectOverride(FALSE),
+ mNeedsAutoRename(FALSE),
+ mShowSelectionContext(FALSE),
+ mShowSingleSelection(FALSE),
+ mArrangeGeneration(0),
+ mSignalSelectCallback(0),
+ mMinWidth(0),
+ mDragAndDropThisFrame(FALSE),
+ mCallbackRegistrar(NULL),
+ mUseEllipses(p.use_ellipses),
+ mDraggingOverItem(NULL),
+ mStatusTextBox(NULL),
+ mShowItemLinkOverlays(p.show_item_link_overlays),
+ mViewModel(p.view_model),
+ mGroupedItemModel(p.grouped_item_model)
+{
+ claimMem(mViewModel);
+ LLPanel* panel = p.parent_panel;
+ mParentPanel = panel->getHandle();
+ mViewModel->setFolderView(this);
+ mRoot = this;
+
+ LLRect rect = p.rect;
+ LLRect new_rect(rect.mLeft, rect.mBottom + getRect().getHeight(), rect.mLeft + getRect().getWidth(), rect.mBottom);
+ setRect( rect );
+ reshape(rect.getWidth(), rect.getHeight());
+ mAutoOpenItems.setDepth(AUTO_OPEN_STACK_DEPTH);
+ mAutoOpenCandidate = NULL;
+ mAutoOpenTimer.stop();
+ mKeyboardSelection = FALSE;
+ mIndentation = getParentFolder() ? getParentFolder()->getIndentation() + mLocalIndentation : 0;
+
+ //clear label
+ // go ahead and render root folder as usual
+ // just make sure the label ("Inventory Folder") never shows up
+ mLabel = LLStringUtil::null;
+
+ // Escape is handled by reverting the rename, not commiting it (default behavior)
+ LLLineEditor::Params params;
+ params.name("ren");
+ params.rect(rect);
+ params.font(getLabelFontForStyle(LLFontGL::NORMAL));
+ params.max_length.bytes(DB_INV_ITEM_NAME_STR_LEN);
+ params.commit_callback.function(boost::bind(&LLFolderView::commitRename, this, _2));
+ params.prevalidate_callback(&LLTextValidate::validateASCIIPrintableNoPipe);
+ params.commit_on_focus_lost(true);
+ params.visible(false);
+ mRenamer = LLUICtrlFactory::create<LLLineEditor> (params);
+ addChild(mRenamer);
+
+ // Textbox
+ LLTextBox::Params text_p;
+ LLFontGL* font = getLabelFontForStyle(mLabelStyle);
+ //mIconPad, mTextPad are set in folder_view_item.xml
+ LLRect new_r = LLRect(rect.mLeft + mIconPad,
+ rect.mTop - mTextPad,
+ rect.mRight,
+ rect.mTop - mTextPad - font->getLineHeight());
+ text_p.rect(new_r);
+ text_p.name(std::string(p.name));
+ text_p.font(font);
+ text_p.visible(false);
+ text_p.parse_urls(true);
+ text_p.wrap(true); // allow multiline text. See EXT-7564, EXT-7047
+ // set text padding the same as in People panel. EXT-7047, EXT-4837
+ text_p.h_pad(STATUS_TEXT_HPAD);
+ text_p.v_pad(STATUS_TEXT_VPAD);
+ mStatusTextBox = LLUICtrlFactory::create<LLTextBox> (text_p);
+ mStatusTextBox->setFollowsLeft();
+ mStatusTextBox->setFollowsTop();
+ addChild(mStatusTextBox);
+
+
+ // make the popup menu available
+ llassert(LLMenuGL::sMenuContainer != NULL);
+ LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>(p.options_menu, LLMenuGL::sMenuContainer, LLMenuHolderGL::child_registry_t::instance());
+ if (!menu)
+ {
+ menu = LLUICtrlFactory::getDefaultWidget<LLMenuGL>("inventory_menu");
+ }
+ menu->setBackgroundColor(LLUIColorTable::instance().getColor("MenuPopupBgColor"));
+ mPopupMenuHandle = menu->getHandle();
+
+ mViewModelItem->openItem();
+}
+
+// Destroys the object
+LLFolderView::~LLFolderView( void )
+{
+ closeRenamer();
+
+ // The release focus call can potentially call the
+ // scrollcontainer, which can potentially be called with a partly
+ // destroyed scollcontainer. Just null it out here, and no worries
+ // about calling into the invalid scroll container.
+ // Same with the renamer.
+ mScrollContainer = NULL;
+ mRenameItem = NULL;
+ mRenamer = NULL;
+ mStatusTextBox = NULL;
+
+ if (mPopupMenuHandle.get()) mPopupMenuHandle.get()->die();
+
+ mAutoOpenItems.removeAllNodes();
+ clearSelection();
+ mItems.clear();
+ mFolders.clear();
+
+ //mViewModel->setFolderView(NULL);
+ mViewModel = NULL;
+}
+
+BOOL LLFolderView::canFocusChildren() const
+{
+ return FALSE;
+}
+
+void LLFolderView::addFolder( LLFolderViewFolder* folder)
+{
+ LLFolderViewFolder::addFolder(folder);
+}
+
+void LLFolderView::closeAllFolders()
+{
+ // Close all the folders
+ setOpenArrangeRecursively(FALSE, LLFolderViewFolder::RECURSE_DOWN);
+ arrangeAll();
+}
+
+void LLFolderView::openTopLevelFolders()
+{
+ for (folders_t::iterator iter = mFolders.begin();
+ iter != mFolders.end();)
+ {
+ folders_t::iterator fit = iter++;
+ (*fit)->setOpen(TRUE);
+ }
+}
+
+// This view grows and shrinks to enclose all of its children items and folders.
+// *width should be 0
+// conform show folder state works
+S32 LLFolderView::arrange( S32* unused_width, S32* unused_height )
+ {
+ mMinWidth = 0;
+ S32 target_height;
+
+ LLFolderViewFolder::arrange(&mMinWidth, &target_height);
+
+ LLRect scroll_rect = (mScrollContainer ? mScrollContainer->getContentWindowRect() : LLRect());
+ reshape( llmax(scroll_rect.getWidth(), mMinWidth), ll_round(mCurHeight) );
+
+ LLRect new_scroll_rect = (mScrollContainer ? mScrollContainer->getContentWindowRect() : LLRect());
+ if (new_scroll_rect.getWidth() != scroll_rect.getWidth())
+ {
+ reshape( llmax(scroll_rect.getWidth(), mMinWidth), ll_round(mCurHeight) );
+ }
+
+ // move item renamer text field to item's new position
+ updateRenamerPosition();
+
+ return ll_round(mTargetHeight);
+}
+
+static LLTrace::BlockTimerStatHandle FTM_FILTER("Filter Folder View");
+
+void LLFolderView::filter( LLFolderViewFilter& filter )
+{
+ LL_RECORD_BLOCK_TIME(FTM_FILTER);
+ filter.resetTime(llclamp(LLUI::sSettingGroups["config"]->getS32(mParentPanel.get()->getVisible() ? "FilterItemsMaxTimePerFrameVisible" : "FilterItemsMaxTimePerFrameUnvisible"), 1, 100));
+
+ // Note: we filter the model, not the view
+ getViewModelItem()->filter(filter);
+}
+
+void LLFolderView::reshape(S32 width, S32 height, BOOL called_from_parent)
+{
+ LLRect scroll_rect;
+ if (mScrollContainer)
+ {
+ LLView::reshape(width, height, called_from_parent);
+ scroll_rect = mScrollContainer->getContentWindowRect();
+ }
+ width = llmax(mMinWidth, scroll_rect.getWidth());
+ height = llmax(ll_round(mCurHeight), scroll_rect.getHeight());
+
+ // Restrict width within scroll container's width
+ if (mUseEllipses && mScrollContainer)
+ {
+ width = scroll_rect.getWidth();
+ }
+ LLView::reshape(width, height, called_from_parent);
+ mReshapeSignal(mSelectedItems, FALSE);
+}
+
+void LLFolderView::addToSelectionList(LLFolderViewItem* item)
+{
+ if (item->isSelected())
+ {
+ removeFromSelectionList(item);
+ }
+ if (mSelectedItems.size())
+ {
+ mSelectedItems.back()->setIsCurSelection(FALSE);
+ }
+ item->setIsCurSelection(TRUE);
+ mSelectedItems.push_back(item);
+}
+
+void LLFolderView::removeFromSelectionList(LLFolderViewItem* item)
+{
+ if (mSelectedItems.size())
+ {
+ mSelectedItems.back()->setIsCurSelection(FALSE);
+ }
+
+ selected_items_t::iterator item_iter;
+ for (item_iter = mSelectedItems.begin(); item_iter != mSelectedItems.end();)
+ {
+ if (*item_iter == item)
+ {
+ item_iter = mSelectedItems.erase(item_iter);
+ }
+ else
+ {
+ ++item_iter;
+ }
+ }
+ if (mSelectedItems.size())
+ {
+ mSelectedItems.back()->setIsCurSelection(TRUE);
+ }
+}
+
+LLFolderViewItem* LLFolderView::getCurSelectedItem( void )
+{
+ if(mSelectedItems.size())
+ {
+ LLFolderViewItem* itemp = mSelectedItems.back();
+ llassert(itemp->getIsCurSelection());
+ return itemp;
+ }
+ return NULL;
+}
+
+LLFolderView::selected_items_t& LLFolderView::getSelectedItems( void )
+{
+ return mSelectedItems;
+}
+
+// Record the selected item and pass it down the hierachy.
+BOOL LLFolderView::setSelection(LLFolderViewItem* selection, BOOL openitem,
+ BOOL take_keyboard_focus)
+{
+ mSignalSelectCallback = take_keyboard_focus ? SIGNAL_KEYBOARD_FOCUS : SIGNAL_NO_KEYBOARD_FOCUS;
+
+ if( selection == this )
+ {
+ return FALSE;
+ }
+
+ if( selection && take_keyboard_focus)
+ {
+ mParentPanel.get()->setFocus(TRUE);
+ }
+
+ // clear selection down here because change of keyboard focus can potentially
+ // affect selection
+ clearSelection();
+
+ if(selection)
+ {
+ addToSelectionList(selection);
+ }
+
+ BOOL rv = LLFolderViewFolder::setSelection(selection, openitem, take_keyboard_focus);
+ if(openitem && selection)
+ {
+ selection->getParentFolder()->requestArrange();
+ }
+
+ llassert(mSelectedItems.size() <= 1);
+
+ return rv;
+}
+
+BOOL LLFolderView::changeSelection(LLFolderViewItem* selection, BOOL selected)
+{
+ BOOL rv = FALSE;
+
+ // can't select root folder
+ if(!selection || selection == this)
+ {
+ return FALSE;
+ }
+
+ if (!mAllowMultiSelect)
+ {
+ clearSelection();
+ }
+
+ selected_items_t::iterator item_iter;
+ for (item_iter = mSelectedItems.begin(); item_iter != mSelectedItems.end(); ++item_iter)
+ {
+ if (*item_iter == selection)
+ {
+ break;
+ }
+ }
+
+ BOOL on_list = (item_iter != mSelectedItems.end());
+
+ if(selected && !on_list)
+ {
+ addToSelectionList(selection);
+ }
+ if(!selected && on_list)
+ {
+ removeFromSelectionList(selection);
+ }
+
+ rv = LLFolderViewFolder::changeSelection(selection, selected);
+
+ mSignalSelectCallback = SIGNAL_KEYBOARD_FOCUS;
+
+ return rv;
+}
+
+static LLTrace::BlockTimerStatHandle FTM_SANITIZE_SELECTION("Sanitize Selection");
+void LLFolderView::sanitizeSelection()
+{
+ LL_RECORD_BLOCK_TIME(FTM_SANITIZE_SELECTION);
+ // store off current item in case it is automatically deselected
+ // and we want to preserve context
+ LLFolderViewItem* original_selected_item = getCurSelectedItem();
+
+ std::vector<LLFolderViewItem*> items_to_remove;
+ selected_items_t::iterator item_iter;
+ for (item_iter = mSelectedItems.begin(); item_iter != mSelectedItems.end(); ++item_iter)
+ {
+ LLFolderViewItem* item = *item_iter;
+
+ // ensure that each ancestor is open and potentially passes filtering
+ BOOL visible = false;
+ if(item->getViewModelItem() != NULL)
+ {
+ visible = item->getViewModelItem()->potentiallyVisible(); // initialize from filter state for this item
+ }
+ // modify with parent open and filters states
+ LLFolderViewFolder* parent_folder = item->getParentFolder();
+ // Move up through parent folders and see what's visible
+ while(parent_folder)
+ {
+ visible = visible && parent_folder->isOpen() && parent_folder->getViewModelItem()->potentiallyVisible();
+ parent_folder = parent_folder->getParentFolder();
+ }
+
+ // deselect item if any ancestor is closed or didn't pass filter requirements.
+ if (!visible)
+ {
+ items_to_remove.push_back(item);
+ }
+
+ // disallow nested selections (i.e. folder items plus one or more ancestors)
+ // could check cached mum selections count and only iterate if there are any
+ // but that may be a premature optimization.
+ selected_items_t::iterator other_item_iter;
+ for (other_item_iter = mSelectedItems.begin(); other_item_iter != mSelectedItems.end(); ++other_item_iter)
+ {
+ LLFolderViewItem* other_item = *other_item_iter;
+ for( parent_folder = other_item->getParentFolder(); parent_folder; parent_folder = parent_folder->getParentFolder())
+ {
+ if (parent_folder == item)
+ {
+ // this is a descendent of the current folder, remove from list
+ items_to_remove.push_back(other_item);
+ break;
+ }
+ }
+ }
+
+ // Don't allow invisible items (such as root folders) to be selected.
+ if (item == getRoot())
+ {
+ items_to_remove.push_back(item);
+ }
+ }
+
+ std::vector<LLFolderViewItem*>::iterator item_it;
+ for (item_it = items_to_remove.begin(); item_it != items_to_remove.end(); ++item_it )
+ {
+ changeSelection(*item_it, FALSE); // toggle selection (also removes from list)
+ }
+
+ // if nothing selected after prior constraints...
+ if (mSelectedItems.empty())
+ {
+ // ...select first available parent of original selection
+ LLFolderViewItem* new_selection = NULL;
+ if (original_selected_item)
+ {
+ for(LLFolderViewFolder* parent_folder = original_selected_item->getParentFolder();
+ parent_folder;
+ parent_folder = parent_folder->getParentFolder())
+ {
+ if (parent_folder->getViewModelItem() && parent_folder->getViewModelItem()->potentiallyVisible())
+ {
+ // give initial selection to first ancestor folder that potentially passes the filter
+ if (!new_selection)
+ {
+ new_selection = parent_folder;
+ }
+
+ // if any ancestor folder of original item is closed, move the selection up
+ // to the highest closed
+ if (!parent_folder->isOpen())
+ {
+ new_selection = parent_folder;
+ }
+ }
+ }
+ }
+ else
+ {
+ new_selection = NULL;
+ }
+
+ if (new_selection)
+ {
+ setSelection(new_selection, FALSE, FALSE);
+ }
+ }
+}
+
+void LLFolderView::clearSelection()
+{
+ for (selected_items_t::const_iterator item_it = mSelectedItems.begin();
+ item_it != mSelectedItems.end();
+ ++item_it)
+ {
+ (*item_it)->setUnselected();
+ }
+
+ mSelectedItems.clear();
+}
+
+std::set<LLFolderViewItem*> LLFolderView::getSelectionList() const
+{
+ std::set<LLFolderViewItem*> selection;
+ std::copy(mSelectedItems.begin(), mSelectedItems.end(), std::inserter(selection, selection.begin()));
+ return selection;
+}
+
+bool LLFolderView::startDrag()
+{
+ std::vector<LLFolderViewModelItem*> selected_items;
+ selected_items_t::iterator item_it;
+
+ if (!mSelectedItems.empty())
+ {
+ for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it)
+ {
+ selected_items.push_back((*item_it)->getViewModelItem());
+ }
+
+ return getFolderViewModel()->startDrag(selected_items);
+ }
+ return false;
+}
+
+void LLFolderView::commitRename( const LLSD& data )
+{
+ finishRenamingItem();
+ arrange( NULL, NULL );
+
+}
+
+void LLFolderView::draw()
+{
+ //LLFontGL* font = getLabelFontForStyle(mLabelStyle);
+
+ // if cursor has moved off of me during drag and drop
+ // close all auto opened folders
+ if (!mDragAndDropThisFrame)
+ {
+ closeAutoOpenedFolders();
+ }
+
+ if (mSearchTimer.getElapsedTimeF32() > LLUI::sSettingGroups["config"]->getF32("TypeAheadTimeout") || !mSearchString.size())
+ {
+ mSearchString.clear();
+ }
+
+ if (hasVisibleChildren())
+ {
+ mStatusTextBox->setVisible( FALSE );
+ }
+ else if (mShowEmptyMessage)
+ {
+ mStatusTextBox->setValue(getFolderViewModel()->getStatusText());
+ mStatusTextBox->setVisible( TRUE );
+
+ // firstly reshape message textbox with current size. This is necessary to
+ // LLTextBox::getTextPixelHeight works properly
+ const LLRect local_rect = getLocalRect();
+ mStatusTextBox->setShape(local_rect);
+
+ // get preferable text height...
+ S32 pixel_height = mStatusTextBox->getTextPixelHeight();
+ bool height_changed = (local_rect.getHeight() < pixel_height);
+ if (height_changed)
+ {
+ // ... if it does not match current height, lets rearrange current view.
+ // This will indirectly call ::arrange and reshape of the status textbox.
+ // We should call this method to also notify parent about required rect.
+ // See EXT-7564, EXT-7047.
+ S32 height = 0;
+ S32 width = 0;
+ S32 total_height = arrange( &width, &height );
+ notifyParent(LLSD().with("action", "size_changes").with("height", total_height));
+
+ LLUI::popMatrix();
+ LLUI::pushMatrix();
+ LLUI::translate((F32)getRect().mLeft, (F32)getRect().mBottom);
+ }
+ }
+
+ // skip over LLFolderViewFolder::draw since we don't want the folder icon, label,
+ // and arrow for the root folder
+ LLView::draw();
+
+ mDragAndDropThisFrame = FALSE;
+}
+
+void LLFolderView::finishRenamingItem( void )
+{
+ if(!mRenamer)
+ {
+ return;
+ }
+ if( mRenameItem )
+ {
+ mRenameItem->rename( mRenamer->getText() );
+ }
+
+ closeRenamer();
+
+ // This is moved to an inventory observer in llinventorybridge.cpp, to handle updating after operation completed in AISv3 (SH-4611).
+ // List is re-sorted alphabetically, so scroll to make sure the selected item is visible.
+ //scrollToShowSelection();
+}
+
+void LLFolderView::closeRenamer( void )
+{
+ if (mRenamer && mRenamer->getVisible())
+ {
+ // Triggers onRenamerLost() that actually closes the renamer.
+ LLUI::removePopup(mRenamer);
+ }
+}
+
+void LLFolderView::removeSelectedItems()
+{
+ if(getVisible() && getEnabled())
+ {
+ // just in case we're removing the renaming item.
+ mRenameItem = NULL;
+
+ // create a temporary structure which we will use to remove
+ // items, since the removal will futz with internal data
+ // structures.
+ std::vector<LLFolderViewItem*> items;
+ S32 count = mSelectedItems.size();
+ if(count == 0) return;
+ LLFolderViewItem* item = NULL;
+ selected_items_t::iterator item_it;
+ for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it)
+ {
+ item = *item_it;
+ if (item && item->isRemovable())
+ {
+ items.push_back(item);
+ }
+ else
+ {
+ LL_INFOS() << "Cannot delete " << item->getName() << LL_ENDL;
+ return;
+ }
+ }
+
+ // iterate through the new container.
+ count = items.size();
+ LLUUID new_selection_id;
+ LLFolderViewItem* item_to_select = getNextUnselectedItem();
+
+ if(count == 1)
+ {
+ LLFolderViewItem* item_to_delete = items[0];
+ LLFolderViewFolder* parent = item_to_delete->getParentFolder();
+ if(parent)
+ {
+ if (item_to_delete->remove())
+ {
+ // change selection on successful delete
+ setSelection(item_to_select, item_to_select ? item_to_select->isOpen() : false, mParentPanel.get()->hasFocus());
+ }
+ }
+ arrangeAll();
+ }
+ else if (count > 1)
+ {
+ std::vector<LLFolderViewModelItem*> listeners;
+ LLFolderViewModelItem* listener;
+
+ setSelection(item_to_select, item_to_select ? item_to_select->isOpen() : false, mParentPanel.get()->hasFocus());
+
+ listeners.reserve(count);
+ for(S32 i = 0; i < count; ++i)
+ {
+ listener = items[i]->getViewModelItem();
+ if(listener && (std::find(listeners.begin(), listeners.end(), listener) == listeners.end()))
+ {
+ listeners.push_back(listener);
+ }
+ }
+ listener = static_cast<LLFolderViewModelItem*>(listeners.at(0));
+ if(listener)
+ {
+ listener->removeBatch(listeners);
+ }
+ }
+ arrangeAll();
+ scrollToShowSelection();
+ }
+}
+
+void LLFolderView::autoOpenItem( LLFolderViewFolder* item )
+{
+ if ((mAutoOpenItems.check() == item) ||
+ (mAutoOpenItems.getDepth() >= (U32)AUTO_OPEN_STACK_DEPTH) ||
+ item->isOpen())
+ {
+ return;
+ }
+
+ // close auto-opened folders
+ LLFolderViewFolder* close_item = mAutoOpenItems.check();
+ while (close_item && close_item != item->getParentFolder())
+ {
+ mAutoOpenItems.pop();
+ close_item->setOpenArrangeRecursively(FALSE);
+ close_item = mAutoOpenItems.check();
+ }
+
+ item->requestArrange();
+
+ mAutoOpenItems.push(item);
+
+ item->setOpen(TRUE);
+ LLRect content_rect = (mScrollContainer ? mScrollContainer->getContentWindowRect() : LLRect());
+ LLRect constraint_rect(0,content_rect.getHeight(), content_rect.getWidth(), 0);
+ scrollToShowItem(item, constraint_rect);
+}
+
+void LLFolderView::closeAutoOpenedFolders()
+{
+ while (mAutoOpenItems.check())
+ {
+ LLFolderViewFolder* close_item = mAutoOpenItems.pop();
+ close_item->setOpen(FALSE);
+ }
+
+ if (mAutoOpenCandidate)
+ {
+ mAutoOpenCandidate->setAutoOpenCountdown(0.f);
+ }
+ mAutoOpenCandidate = NULL;
+ mAutoOpenTimer.stop();
+}
+
+BOOL LLFolderView::autoOpenTest(LLFolderViewFolder* folder)
+{
+ if (folder && mAutoOpenCandidate == folder)
+ {
+ if (mAutoOpenTimer.getStarted())
+ {
+ if (!mAutoOpenCandidate->isOpen())
+ {
+ mAutoOpenCandidate->setAutoOpenCountdown(clamp_rescale(mAutoOpenTimer.getElapsedTimeF32(), 0.f, sAutoOpenTime, 0.f, 1.f));
+ }
+ if (mAutoOpenTimer.getElapsedTimeF32() > sAutoOpenTime)
+ {
+ autoOpenItem(folder);
+ mAutoOpenTimer.stop();
+ return TRUE;
+ }
+ }
+ return FALSE;
+ }
+
+ // otherwise new candidate, restart timer
+ if (mAutoOpenCandidate)
+ {
+ mAutoOpenCandidate->setAutoOpenCountdown(0.f);
+ }
+ mAutoOpenCandidate = folder;
+ mAutoOpenTimer.start();
+ return FALSE;
+}
+
+BOOL LLFolderView::canCopy() const
+{
+ if (!(getVisible() && getEnabled() && (mSelectedItems.size() > 0)))
+ {
+ return FALSE;
+ }
+
+ for (selected_items_t::const_iterator selected_it = mSelectedItems.begin(); selected_it != mSelectedItems.end(); ++selected_it)
+ {
+ const LLFolderViewItem* item = *selected_it;
+ if (!item->getViewModelItem()->isItemCopyable())
+ {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+// copy selected item
+void LLFolderView::copy()
+{
+ // *NOTE: total hack to clear the inventory clipboard
+ LLClipboard::instance().reset();
+ S32 count = mSelectedItems.size();
+ if(getVisible() && getEnabled() && (count > 0))
+ {
+ LLFolderViewModelItem* listener = NULL;
+ selected_items_t::iterator item_it;
+ for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it)
+ {
+ listener = (*item_it)->getViewModelItem();
+ if(listener)
+ {
+ listener->copyToClipboard();
+ }
+ }
+ }
+ mSearchString.clear();
+}
+
+BOOL LLFolderView::canCut() const
+{
+ if (!(getVisible() && getEnabled() && (mSelectedItems.size() > 0)))
+ {
+ return FALSE;
+ }
+
+ for (selected_items_t::const_iterator selected_it = mSelectedItems.begin(); selected_it != mSelectedItems.end(); ++selected_it)
+ {
+ const LLFolderViewItem* item = *selected_it;
+ const LLFolderViewModelItem* listener = item->getViewModelItem();
+
+ if (!listener || !listener->isItemRemovable())
+ {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+void LLFolderView::cut()
+{
+ // clear the inventory clipboard
+ LLClipboard::instance().reset();
+ if(getVisible() && getEnabled() && (mSelectedItems.size() > 0))
+ {
+ // Find out which item will be selected once the selection will be cut
+ LLFolderViewItem* item_to_select = getNextUnselectedItem();
+
+ // Get the selection: removeItem() modified mSelectedItems and makes iterating on it unwise
+ std::set<LLFolderViewItem*> inventory_selected = getSelectionList();
+
+ // Move each item to the clipboard and out of their folder
+ for (std::set<LLFolderViewItem*>::iterator item_it = inventory_selected.begin(); item_it != inventory_selected.end(); ++item_it)
+ {
+ LLFolderViewItem* item_to_cut = *item_it;
+ LLFolderViewModelItem* listener = item_to_cut->getViewModelItem();
+ if (listener)
+ {
+ listener->cutToClipboard();
+ listener->removeItem();
+ }
+ }
+
+ // Update the selection
+ setSelection(item_to_select, item_to_select ? item_to_select->isOpen() : false, mParentPanel.get()->hasFocus());
+ }
+ mSearchString.clear();
+}
+
+BOOL LLFolderView::canPaste() const
+{
+ if (mSelectedItems.empty())
+ {
+ return FALSE;
+ }
+
+ if(getVisible() && getEnabled())
+ {
+ for (selected_items_t::const_iterator item_it = mSelectedItems.begin();
+ item_it != mSelectedItems.end(); ++item_it)
+ {
+ // *TODO: only check folders and parent folders of items
+ const LLFolderViewItem* item = (*item_it);
+ const LLFolderViewModelItem* listener = item->getViewModelItem();
+ if(!listener || !listener->isClipboardPasteable())
+ {
+ const LLFolderViewFolder* folderp = item->getParentFolder();
+ listener = folderp->getViewModelItem();
+ if (!listener || !listener->isClipboardPasteable())
+ {
+ return FALSE;
+ }
+ }
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+// paste selected item
+void LLFolderView::paste()
+{
+ if(getVisible() && getEnabled())
+ {
+ // find set of unique folders to paste into
+ std::set<LLFolderViewFolder*> folder_set;
+
+ selected_items_t::iterator selected_it;
+ for (selected_it = mSelectedItems.begin(); selected_it != mSelectedItems.end(); ++selected_it)
+ {
+ LLFolderViewItem* item = *selected_it;
+ LLFolderViewFolder* folder = dynamic_cast<LLFolderViewFolder*>(item);
+ if (folder == NULL)
+ {
+ folder = item->getParentFolder();
+ }
+ folder_set.insert(folder);
+ }
+
+ std::set<LLFolderViewFolder*>::iterator set_iter;
+ for(set_iter = folder_set.begin(); set_iter != folder_set.end(); ++set_iter)
+ {
+ LLFolderViewModelItem* listener = (*set_iter)->getViewModelItem();
+ if(listener && listener->isClipboardPasteable())
+ {
+ listener->pasteFromClipboard();
+ }
+ }
+ }
+ mSearchString.clear();
+}
+
+// public rename functionality - can only start the process
+void LLFolderView::startRenamingSelectedItem( void )
+{
+ // make sure selection is visible
+ scrollToShowSelection();
+
+ S32 count = mSelectedItems.size();
+ LLFolderViewItem* item = NULL;
+ if(count > 0)
+ {
+ item = mSelectedItems.front();
+ }
+ if(getVisible() && getEnabled() && (count == 1) && item && item->getViewModelItem() &&
+ item->getViewModelItem()->isItemRenameable())
+ {
+ mRenameItem = item;
+
+ updateRenamerPosition();
+
+
+ mRenamer->setText(item->getName());
+ mRenamer->selectAll();
+ mRenamer->setVisible( TRUE );
+ // set focus will fail unless item is visible
+ mRenamer->setFocus( TRUE );
+ mRenamer->setTopLostCallback(boost::bind(&LLFolderView::onRenamerLost, this));
+ LLUI::addPopup(mRenamer);
+ }
+}
+
+BOOL LLFolderView::handleKeyHere( KEY key, MASK mask )
+{
+ BOOL handled = FALSE;
+
+ // SL-51858: Key presses are not being passed to the Popup menu.
+ // A proper fix is non-trivial so instead just close the menu.
+ LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandle.get();
+ if (menu && menu->isOpen())
+ {
+ LLMenuGL::sMenuContainer->hideMenus();
+ }
+
+ switch( key )
+ {
+ case KEY_F2:
+ mSearchString.clear();
+ startRenamingSelectedItem();
+ handled = TRUE;
+ break;
+
+ case KEY_RETURN:
+ if (mask == MASK_NONE)
+ {
+ if( mRenameItem && mRenamer->getVisible() )
+ {
+ finishRenamingItem();
+ mSearchString.clear();
+ handled = TRUE;
+ }
+ }
+ break;
+
+ case KEY_ESCAPE:
+ if( mRenameItem && mRenamer->getVisible() )
+ {
+ closeRenamer();
+ handled = TRUE;
+ }
+ mSearchString.clear();
+ break;
+
+ case KEY_PAGE_UP:
+ mSearchString.clear();
+ if (mScrollContainer)
+ {
+ mScrollContainer->pageUp(30);
+ }
+ handled = TRUE;
+ break;
+
+ case KEY_PAGE_DOWN:
+ mSearchString.clear();
+ if (mScrollContainer)
+ {
+ mScrollContainer->pageDown(30);
+ }
+ handled = TRUE;
+ break;
+
+ case KEY_HOME:
+ mSearchString.clear();
+ if (mScrollContainer)
+ {
+ mScrollContainer->goToTop();
+ }
+ handled = TRUE;
+ break;
+
+ case KEY_END:
+ mSearchString.clear();
+ if (mScrollContainer)
+ {
+ mScrollContainer->goToBottom();
+ }
+ break;
+
+ case KEY_DOWN:
+ if((mSelectedItems.size() > 0) && mScrollContainer)
+ {
+ LLFolderViewItem* last_selected = getCurSelectedItem();
+ BOOL shift_select = mask & MASK_SHIFT;
+ // don't shift select down to children of folders (they are implicitly selected through parent)
+ LLFolderViewItem* next = last_selected->getNextOpenNode(!shift_select);
+
+ if (!mKeyboardSelection || (!shift_select && (!next || next == last_selected)))
+ {
+ setSelection(last_selected, FALSE, TRUE);
+ mKeyboardSelection = TRUE;
+ }
+
+ if (shift_select)
+ {
+ if (next)
+ {
+ if (next->isSelected())
+ {
+ // shrink selection
+ changeSelection(last_selected, FALSE);
+ }
+ else if (last_selected->getParentFolder() == next->getParentFolder())
+ {
+ // grow selection
+ changeSelection(next, TRUE);
+ }
+ }
+ }
+ else
+ {
+ if( next )
+ {
+ if (next == last_selected)
+ {
+ //special case for LLAccordionCtrl
+ if(notifyParent(LLSD().with("action","select_next")) > 0 )//message was processed
+ {
+ clearSelection();
+ return TRUE;
+ }
+ return FALSE;
+ }
+ setSelection( next, FALSE, TRUE );
+ }
+ else
+ {
+ //special case for LLAccordionCtrl
+ if(notifyParent(LLSD().with("action","select_next")) > 0 )//message was processed
+ {
+ clearSelection();
+ return TRUE;
+ }
+ return FALSE;
+ }
+ }
+ scrollToShowSelection();
+ mSearchString.clear();
+ handled = TRUE;
+ }
+ break;
+
+ case KEY_UP:
+ if((mSelectedItems.size() > 0) && mScrollContainer)
+ {
+ LLFolderViewItem* last_selected = mSelectedItems.back();
+ BOOL shift_select = mask & MASK_SHIFT;
+ // don't shift select down to children of folders (they are implicitly selected through parent)
+ LLFolderViewItem* prev = last_selected->getPreviousOpenNode(!shift_select);
+
+ if (!mKeyboardSelection || (!shift_select && prev == this))
+ {
+ setSelection(last_selected, FALSE, TRUE);
+ mKeyboardSelection = TRUE;
+ }
+
+ if (shift_select)
+ {
+ if (prev)
+ {
+ if (prev->isSelected())
+ {
+ // shrink selection
+ changeSelection(last_selected, FALSE);
+ }
+ else if (last_selected->getParentFolder() == prev->getParentFolder())
+ {
+ // grow selection
+ changeSelection(prev, TRUE);
+ }
+ }
+ }
+ else
+ {
+ if( prev )
+ {
+ if (prev == this)
+ {
+ // If case we are in accordion tab notify parent to go to the previous accordion
+ if(notifyParent(LLSD().with("action","select_prev")) > 0 )//message was processed
+ {
+ clearSelection();
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+ setSelection( prev, FALSE, TRUE );
+ }
+ }
+ scrollToShowSelection();
+ mSearchString.clear();
+
+ handled = TRUE;
+ }
+ break;
+
+ case KEY_RIGHT:
+ if(mSelectedItems.size())
+ {
+ LLFolderViewItem* last_selected = getCurSelectedItem();
+ last_selected->setOpen( TRUE );
+ mSearchString.clear();
+ handled = TRUE;
+ }
+ break;
+
+ case KEY_LEFT:
+ if(mSelectedItems.size())
+ {
+ LLFolderViewItem* last_selected = getCurSelectedItem();
+ LLFolderViewItem* parent_folder = last_selected->getParentFolder();
+ if (!last_selected->isOpen() && parent_folder && parent_folder->getParentFolder())
+ {
+ setSelection(parent_folder, FALSE, TRUE);
+ }
+ else
+ {
+ last_selected->setOpen( FALSE );
+ }
+ mSearchString.clear();
+ scrollToShowSelection();
+ handled = TRUE;
+ }
+ break;
+ }
+
+ return handled;
+}
+
+
+BOOL LLFolderView::handleUnicodeCharHere(llwchar uni_char)
+{
+ if ((uni_char < 0x20) || (uni_char == 0x7F)) // Control character or DEL
+ {
+ return FALSE;
+ }
+
+ if (uni_char > 0x7f)
+ {
+ LL_WARNS() << "LLFolderView::handleUnicodeCharHere - Don't handle non-ascii yet, aborting" << LL_ENDL;
+ return FALSE;
+ }
+
+ BOOL handled = FALSE;
+ if (mParentPanel.get()->hasFocus())
+ {
+ // SL-51858: Key presses are not being passed to the Popup menu.
+ // A proper fix is non-trivial so instead just close the menu.
+ LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandle.get();
+ if (menu && menu->isOpen())
+ {
+ LLMenuGL::sMenuContainer->hideMenus();
+ }
+
+ //do text search
+ if (mSearchTimer.getElapsedTimeF32() > LLUI::sSettingGroups["config"]->getF32("TypeAheadTimeout"))
+ {
+ mSearchString.clear();
+ }
+ mSearchTimer.reset();
+ if (mSearchString.size() < 128)
+ {
+ mSearchString += uni_char;
+ }
+ search(getCurSelectedItem(), mSearchString, FALSE);
+
+ handled = TRUE;
+ }
+
+ return handled;
+}
+
+
+BOOL LLFolderView::handleMouseDown( S32 x, S32 y, MASK mask )
+{
+ mKeyboardSelection = FALSE;
+ mSearchString.clear();
+
+ mParentPanel.get()->setFocus(TRUE);
+
+ LLEditMenuHandler::gEditMenuHandler = this;
+
+ return LLView::handleMouseDown( x, y, mask );
+}
+
+BOOL LLFolderView::search(LLFolderViewItem* first_item, const std::string &search_string, BOOL backward)
+{
+ // get first selected item
+ LLFolderViewItem* search_item = first_item;
+
+ // make sure search string is upper case
+ std::string upper_case_string = search_string;
+ LLStringUtil::toUpper(upper_case_string);
+
+ // if nothing selected, select first item in folder
+ if (!search_item)
+ {
+ // start from first item
+ search_item = getNextFromChild(NULL);
+ }
+
+ // search over all open nodes for first substring match (with wrapping)
+ BOOL found = FALSE;
+ LLFolderViewItem* original_search_item = search_item;
+ do
+ {
+ // wrap at end
+ if (!search_item)
+ {
+ if (backward)
+ {
+ search_item = getPreviousFromChild(NULL);
+ }
+ else
+ {
+ search_item = getNextFromChild(NULL);
+ }
+ if (!search_item || search_item == original_search_item)
+ {
+ break;
+ }
+ }
+
+ std::string current_item_label(search_item->getViewModelItem()->getSearchableName());
+ LLStringUtil::toUpper(current_item_label);
+ S32 search_string_length = llmin(upper_case_string.size(), current_item_label.size());
+ if (!current_item_label.compare(0, search_string_length, upper_case_string))
+ {
+ found = TRUE;
+ break;
+ }
+ if (backward)
+ {
+ search_item = search_item->getPreviousOpenNode();
+ }
+ else
+ {
+ search_item = search_item->getNextOpenNode();
+ }
+
+ } while(search_item != original_search_item);
+
+
+ if (found)
+ {
+ setSelection(search_item, FALSE, TRUE);
+ scrollToShowSelection();
+ }
+
+ return found;
+}
+
+BOOL LLFolderView::handleDoubleClick( S32 x, S32 y, MASK mask )
+{
+ // skip LLFolderViewFolder::handleDoubleClick()
+ return LLView::handleDoubleClick( x, y, mask );
+}
+
+BOOL LLFolderView::handleRightMouseDown( S32 x, S32 y, MASK mask )
+{
+ // all user operations move keyboard focus to inventory
+ // this way, we know when to stop auto-updating a search
+ mParentPanel.get()->setFocus(TRUE);
+
+ BOOL handled = childrenHandleRightMouseDown(x, y, mask) != NULL;
+ S32 count = mSelectedItems.size();
+ LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandle.get();
+ if ( handled
+ && ( count > 0 && (hasVisibleChildren()) ) // show menu only if selected items are visible
+ && menu )
+ {
+ if (mCallbackRegistrar)
+ {
+ mCallbackRegistrar->pushScope();
+ }
+
+ updateMenuOptions(menu);
+
+ menu->updateParent(LLMenuGL::sMenuContainer);
+ LLMenuGL::showPopup(this, menu, x, y);
+ if (mCallbackRegistrar)
+ {
+ mCallbackRegistrar->popScope();
+ }
+ }
+ else
+ {
+ if (menu && menu->getVisible())
+ {
+ menu->setVisible(FALSE);
+ }
+ setSelection(NULL, FALSE, TRUE);
+ }
+ return handled;
+}
+
+// Add "--no options--" if the menu is completely blank.
+BOOL LLFolderView::addNoOptions(LLMenuGL* menu) const
+{
+ const std::string nooptions_str = "--no options--";
+ LLView *nooptions_item = NULL;
+
+ const LLView::child_list_t *list = menu->getChildList();
+ for (LLView::child_list_t::const_iterator itor = list->begin();
+ itor != list->end();
+ ++itor)
+ {
+ LLView *menu_item = (*itor);
+ if (menu_item->getVisible())
+ {
+ return FALSE;
+ }
+ std::string name = menu_item->getName();
+ if (menu_item->getName() == nooptions_str)
+ {
+ nooptions_item = menu_item;
+ }
+ }
+ if (nooptions_item)
+ {
+ nooptions_item->setVisible(TRUE);
+ nooptions_item->setEnabled(FALSE);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL LLFolderView::handleHover( S32 x, S32 y, MASK mask )
+{
+ return LLView::handleHover( x, y, mask );
+}
+
+BOOL LLFolderView::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ std::string& tooltip_msg)
+{
+ mDragAndDropThisFrame = TRUE;
+ // have children handle it first
+ BOOL handled = LLView::handleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data,
+ accept, tooltip_msg);
+
+ // when drop is not handled by child, it should be handled
+ // by the folder which is the hierarchy root.
+ if (!handled)
+ {
+ handled = LLFolderViewFolder::handleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg);
+ }
+
+ return handled;
+}
+
+void LLFolderView::deleteAllChildren()
+{
+ closeRenamer();
+ if (mPopupMenuHandle.get()) mPopupMenuHandle.get()->die();
+ mPopupMenuHandle = LLHandle<LLView>();
+ mScrollContainer = NULL;
+ mRenameItem = NULL;
+ mRenamer = NULL;
+ mStatusTextBox = NULL;
+
+ clearSelection();
+ LLView::deleteAllChildren();
+}
+
+void LLFolderView::scrollToShowSelection()
+{
+ if ( mSelectedItems.size() )
+ {
+ mNeedsScroll = TRUE;
+ }
+}
+
+// If the parent is scroll container, scroll it to make the selection
+// is maximally visible.
+void LLFolderView::scrollToShowItem(LLFolderViewItem* item, const LLRect& constraint_rect)
+{
+ if (!mScrollContainer) return;
+
+ // don't scroll to items when mouse is being used to scroll/drag and drop
+ if (gFocusMgr.childHasMouseCapture(mScrollContainer))
+ {
+ mNeedsScroll = FALSE;
+ return;
+ }
+
+ // if item exists and is in visible portion of parent folder...
+ if(item)
+ {
+ LLRect local_rect = item->getLocalRect();
+ S32 icon_height = mIcon.isNull() ? 0 : mIcon->getHeight();
+ S32 label_height = getLabelFontForStyle(mLabelStyle)->getLineHeight();
+ // when navigating with keyboard, only move top of opened folder on screen, otherwise show whole folder
+ S32 max_height_to_show = item->isOpen() && mScrollContainer->hasFocus() ? (llmax( icon_height, label_height ) + item->getIconPad()) : local_rect.getHeight();
+
+ // get portion of item that we want to see...
+ LLRect item_local_rect = LLRect(item->getIndentation(),
+ local_rect.getHeight(),
+ //+40 is supposed to include few first characters
+ llmin(item->getLabelXPos() - item->getIndentation() + 40, local_rect.getWidth()),
+ llmax(0, local_rect.getHeight() - max_height_to_show));
+
+ LLRect item_doc_rect;
+
+ item->localRectToOtherView(item_local_rect, &item_doc_rect, this);
+
+ mScrollContainer->scrollToShowRect( item_doc_rect, constraint_rect );
+
+ }
+}
+
+LLRect LLFolderView::getVisibleRect()
+{
+ S32 visible_height = (mScrollContainer ? mScrollContainer->getRect().getHeight() : 0);
+ S32 visible_width = (mScrollContainer ? mScrollContainer->getRect().getWidth() : 0);
+ LLRect visible_rect;
+ visible_rect.setLeftTopAndSize(-getRect().mLeft, visible_height - getRect().mBottom, visible_width, visible_height);
+ return visible_rect;
+}
+
+BOOL LLFolderView::getShowSelectionContext()
+{
+ if (mShowSelectionContext)
+ {
+ return TRUE;
+ }
+ LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandle.get();
+ if (menu && menu->getVisible())
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void LLFolderView::setShowSingleSelection(bool show)
+{
+ if (show != mShowSingleSelection)
+ {
+ mMultiSelectionFadeTimer.reset();
+ mShowSingleSelection = show;
+ }
+}
+
+static LLTrace::BlockTimerStatHandle FTM_AUTO_SELECT("Open and Select");
+static LLTrace::BlockTimerStatHandle FTM_INVENTORY("Inventory");
+
+// Main idle routine
+void LLFolderView::update()
+{
+ // If this is associated with the user's inventory, don't do anything
+ // until that inventory is loaded up.
+ LL_RECORD_BLOCK_TIME(FTM_INVENTORY);
+
+ // If there's no model, the view is in suspended state (being deleted) and shouldn't be updated
+ if (getFolderViewModel() == NULL)
+ {
+ return;
+ }
+
+ LLFolderViewFilter& filter_object = getFolderViewModel()->getFilter();
+
+ if (filter_object.isModified() && filter_object.isNotDefault() && mParentPanel.get()->getVisible())
+ {
+ mNeedsAutoSelect = TRUE;
+ }
+
+ // Filter to determine visibility before arranging
+ filter(filter_object);
+
+ // Clear the modified setting on the filter only if the filter finished after running the filter process
+ // Note: if the filter count has timed out, that means the filter halted before completing the entire set of items
+ if (filter_object.isModified() && (!filter_object.isTimedOut()))
+ {
+ filter_object.clearModified();
+ }
+
+ // automatically show matching items, and select first one if we had a selection
+ if (mNeedsAutoSelect)
+ {
+ LL_RECORD_BLOCK_TIME(FTM_AUTO_SELECT);
+ // select new item only if a filtered item not currently selected
+ LLFolderViewItem* selected_itemp = mSelectedItems.empty() ? NULL : mSelectedItems.back();
+ if (!mAutoSelectOverride && (!selected_itemp || !selected_itemp->getViewModelItem()->potentiallyVisible()))
+ {
+ // these are named variables to get around gcc not binding non-const references to rvalues
+ // and functor application is inherently non-const to allow for stateful functors
+ LLSelectFirstFilteredItem functor;
+ applyFunctorRecursively(functor);
+ }
+
+ // Open filtered folders for folder views with mAutoSelectOverride=TRUE.
+ // Used by LLPlacesFolderView.
+ if (filter_object.showAllResults())
+ {
+ // these are named variables to get around gcc not binding non-const references to rvalues
+ // and functor application is inherently non-const to allow for stateful functors
+ LLOpenFilteredFolders functor;
+ applyFunctorRecursively(functor);
+ }
+
+ scrollToShowSelection();
+ }
+
+ BOOL filter_finished = mViewModel->contentsReady()
+ && (getViewModelItem()->passedFilter()
+ || ( getViewModelItem()->getLastFilterGeneration() >= filter_object.getFirstSuccessGeneration()
+ && !filter_object.isModified()));
+ if (filter_finished
+ || gFocusMgr.childHasKeyboardFocus(mParentPanel.get())
+ || gFocusMgr.childHasMouseCapture(mParentPanel.get()))
+ {
+ // finishing the filter process, giving focus to the folder view, or dragging the scrollbar all stop the auto select process
+ mNeedsAutoSelect = FALSE;
+ }
+
+ BOOL is_visible = isInVisibleChain();
+
+ //Puts folders/items in proper positions
+ // arrange() takes the model filter flag into account and call sort() if necessary (CHUI-849)
+ // It also handles the open/close folder animation
+ if ( is_visible )
+ {
+ sanitizeSelection();
+ if( needsArrange() )
+ {
+ S32 height = 0;
+ S32 width = 0;
+ S32 total_height = arrange( &width, &height );
+ notifyParent(LLSD().with("action", "size_changes").with("height", total_height));
+ }
+ }
+
+ // during filtering process, try to pin selected item's location on screen
+ // this will happen when searching your inventory and when new items arrive
+ if (!filter_finished)
+ {
+ // calculate rectangle to pin item to at start of animated rearrange
+ if (!mPinningSelectedItem && !mSelectedItems.empty())
+ {
+ // lets pin it!
+ mPinningSelectedItem = TRUE;
+
+ //Computes visible area
+ const LLRect visible_content_rect = (mScrollContainer ? mScrollContainer->getVisibleContentRect() : LLRect());
+ LLFolderViewItem* selected_item = mSelectedItems.back();
+
+ //Computes location of selected content, content outside visible area will be scrolled to using below code
+ LLRect item_rect;
+ selected_item->localRectToOtherView(selected_item->getLocalRect(), &item_rect, this);
+
+ //Computes intersected region of the selected content and visible area
+ LLRect overlap_rect(item_rect);
+ overlap_rect.intersectWith(visible_content_rect);
+
+ //Don't scroll when the selected content exists within the visible area
+ if (overlap_rect.getHeight() >= selected_item->getItemHeight())
+ {
+ // then attempt to keep it in same place on screen
+ mScrollConstraintRect = item_rect;
+ mScrollConstraintRect.translate(-visible_content_rect.mLeft, -visible_content_rect.mBottom);
+ }
+ //Scroll because the selected content is outside the visible area
+ else
+ {
+ // otherwise we just want it onscreen somewhere
+ LLRect content_rect = (mScrollContainer ? mScrollContainer->getContentWindowRect() : LLRect());
+ mScrollConstraintRect.setOriginAndSize(0, 0, content_rect.getWidth(), content_rect.getHeight());
+ }
+ }
+ }
+ else
+ {
+ // stop pinning selected item after folders stop rearranging
+ if (!needsArrange())
+ {
+ mPinningSelectedItem = FALSE;
+ }
+ }
+
+ LLRect constraint_rect;
+ if (mPinningSelectedItem)
+ {
+ // use last known constraint rect for pinned item
+ constraint_rect = mScrollConstraintRect;
+ }
+ else
+ {
+ // during normal use (page up/page down, etc), just try to fit item on screen
+ LLRect content_rect = (mScrollContainer ? mScrollContainer->getContentWindowRect() : LLRect());
+ constraint_rect.setOriginAndSize(0, 0, content_rect.getWidth(), content_rect.getHeight());
+ }
+
+ if (mSelectedItems.size() && mNeedsScroll)
+ {
+ scrollToShowItem(mSelectedItems.back(), constraint_rect);
+ // continue scrolling until animated layout change is done
+ if (filter_finished
+ && (!needsArrange() || !is_visible))
+ {
+ mNeedsScroll = FALSE;
+ }
+ }
+
+ if (mSignalSelectCallback)
+ {
+ //RN: we use keyboard focus as a proxy for user-explicit actions
+ BOOL take_keyboard_focus = (mSignalSelectCallback == SIGNAL_KEYBOARD_FOCUS);
+ mSelectSignal(mSelectedItems, take_keyboard_focus);
+ }
+ mSignalSelectCallback = FALSE;
+}
+
+void LLFolderView::dumpSelectionInformation()
+{
+ LL_INFOS() << "LLFolderView::dumpSelectionInformation()" << LL_NEWLINE
+ << "****************************************" << LL_ENDL;
+ selected_items_t::iterator item_it;
+ for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it)
+ {
+ LL_INFOS() << " " << (*item_it)->getName() << LL_ENDL;
+ }
+ LL_INFOS() << "****************************************" << LL_ENDL;
+}
+
+void LLFolderView::updateRenamerPosition()
+{
+ if(mRenameItem)
+ {
+ // See also LLFolderViewItem::draw()
+ S32 x = mRenameItem->getLabelXPos();
+ S32 y = mRenameItem->getRect().getHeight() - mRenameItem->getItemHeight() - RENAME_HEIGHT_PAD;
+ mRenameItem->localPointToScreen( x, y, &x, &y );
+ screenPointToLocal( x, y, &x, &y );
+ mRenamer->setOrigin( x, y );
+
+ LLRect scroller_rect(0, 0, (S32)LLUI::getWindowSize().mV[VX], 0);
+ if (mScrollContainer)
+ {
+ scroller_rect = mScrollContainer->getContentWindowRect();
+ }
+
+ S32 width = llmax(llmin(mRenameItem->getRect().getWidth() - x, scroller_rect.getWidth() - x - getRect().mLeft), MINIMUM_RENAMER_WIDTH);
+ S32 height = mRenameItem->getItemHeight() - RENAME_HEIGHT_PAD;
+ mRenamer->reshape( width, height, TRUE );
+ }
+}
+
+// Update visibility and availability (i.e. enabled/disabled) of context menu items.
+void LLFolderView::updateMenuOptions(LLMenuGL* menu)
+{
+ const LLView::child_list_t *list = menu->getChildList();
+
+ LLView::child_list_t::const_iterator menu_itor;
+ for (menu_itor = list->begin(); menu_itor != list->end(); ++menu_itor)
+ {
+ (*menu_itor)->setVisible(FALSE);
+ (*menu_itor)->pushVisible(TRUE);
+ (*menu_itor)->setEnabled(TRUE);
+ }
+
+ // Successively filter out invalid options
+ U32 multi_select_flag = (mSelectedItems.size() > 1 ? ITEM_IN_MULTI_SELECTION : 0x0);
+ U32 flags = multi_select_flag | FIRST_SELECTED_ITEM;
+ for (selected_items_t::iterator item_itor = mSelectedItems.begin();
+ item_itor != mSelectedItems.end();
+ ++item_itor)
+ {
+ LLFolderViewItem* selected_item = (*item_itor);
+ selected_item->buildContextMenu(*menu, flags);
+ flags = multi_select_flag;
+ }
+
+ // This adds a check for restrictions based on the entire
+ // selection set - for example, any one wearable may not push you
+ // over the limit, but all wearables together still might.
+ if (getFolderViewGroupedItemModel())
+ {
+ getFolderViewGroupedItemModel()->groupFilterContextMenu(mSelectedItems,*menu);
+ }
+
+ addNoOptions(menu);
+}
+
+// Refresh the context menu (that is already shown).
+void LLFolderView::updateMenu()
+{
+ LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandle.get();
+ if (menu && menu->getVisible())
+ {
+ updateMenuOptions(menu);
+ menu->needsArrange(); // update menu height if needed
+ }
+}
+
+bool LLFolderView::selectFirstItem()
+{
+ for (folders_t::iterator iter = mFolders.begin();
+ iter != mFolders.end();++iter)
+ {
+ LLFolderViewFolder* folder = (*iter );
+ if (folder->getVisible())
+ {
+ LLFolderViewItem* itemp = folder->getNextFromChild(0,true);
+ if(itemp)
+ setSelection(itemp,FALSE,TRUE);
+ return true;
+ }
+
+ }
+ for(items_t::iterator iit = mItems.begin();
+ iit != mItems.end(); ++iit)
+ {
+ LLFolderViewItem* itemp = (*iit);
+ if (itemp->getVisible())
+ {
+ setSelection(itemp,FALSE,TRUE);
+ return true;
+ }
+ }
+ return false;
+}
+bool LLFolderView::selectLastItem()
+{
+ for(items_t::reverse_iterator iit = mItems.rbegin();
+ iit != mItems.rend(); ++iit)
+ {
+ LLFolderViewItem* itemp = (*iit);
+ if (itemp->getVisible())
+ {
+ setSelection(itemp,FALSE,TRUE);
+ return true;
+ }
+ }
+ for (folders_t::reverse_iterator iter = mFolders.rbegin();
+ iter != mFolders.rend();++iter)
+ {
+ LLFolderViewFolder* folder = (*iter);
+ if (folder->getVisible())
+ {
+ LLFolderViewItem* itemp = folder->getPreviousFromChild(0,true);
+ if(itemp)
+ setSelection(itemp,FALSE,TRUE);
+ return true;
+ }
+ }
+ return false;
+}
+
+
+S32 LLFolderView::notify(const LLSD& info)
+{
+ if(info.has("action"))
+ {
+ std::string str_action = info["action"];
+ if(str_action == "select_first")
+ {
+ setFocus(true);
+ selectFirstItem();
+ scrollToShowSelection();
+ return 1;
+
+ }
+ else if(str_action == "select_last")
+ {
+ setFocus(true);
+ selectLastItem();
+ scrollToShowSelection();
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+///----------------------------------------------------------------------------
+/// Local function definitions
+///----------------------------------------------------------------------------
+
+void LLFolderView::onRenamerLost()
+{
+ if (mRenamer && mRenamer->getVisible())
+ {
+ mRenamer->setVisible(FALSE);
+
+ // will commit current name (which could be same as original name)
+ mRenamer->setFocus(FALSE);
+ }
+
+ if( mRenameItem )
+ {
+ setSelection( mRenameItem, TRUE );
+ mRenameItem = NULL;
+ }
+}
+
+LLFolderViewItem* LLFolderView::getNextUnselectedItem()
+{
+ LLFolderViewItem* last_item = *mSelectedItems.rbegin();
+ LLFolderViewItem* new_selection = last_item->getNextOpenNode(FALSE);
+ while(new_selection && new_selection->isSelected())
+ {
+ new_selection = new_selection->getNextOpenNode(FALSE);
+ }
+ if (!new_selection)
+ {
+ new_selection = last_item->getPreviousOpenNode(FALSE);
+ while (new_selection && (new_selection->isInSelection()))
+ {
+ new_selection = new_selection->getPreviousOpenNode(FALSE);
+ }
+ }
+ return new_selection;
+}
+
+S32 LLFolderView::getItemHeight()
+{
+ if(!hasVisibleChildren())
+{
+ //We need to display status textbox, let's reserve some place for it
+ return llmax(0, mStatusTextBox->getTextPixelHeight());
+}
+ return 0;
+}
diff --git a/indra/llui/llfolderview.h b/indra/llui/llfolderview.h
new file mode 100755
index 0000000000..114dd7bd2f
--- /dev/null
+++ b/indra/llui/llfolderview.h
@@ -0,0 +1,407 @@
+/**
+ * @file llfolderview.h
+ * @brief Definition of the folder view collection of classes.
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+/**
+ *
+ * The folder view collection of classes provides an interface for
+ * making a 'folder view' similar to the way the a single pane file
+ * folder interface works.
+ *
+ */
+
+#ifndef LL_LLFOLDERVIEW_H
+#define LL_LLFOLDERVIEW_H
+
+#include "llfolderviewitem.h" // because LLFolderView is-a LLFolderViewFolder
+
+#include "lluictrl.h"
+#include "v4color.h"
+#include "lldepthstack.h"
+#include "lleditmenuhandler.h"
+#include "llfontgl.h"
+#include "llscrollcontainer.h"
+
+class LLFolderViewModelInterface;
+class LLFolderViewGroupedItemModel;
+class LLFolderViewFolder;
+class LLFolderViewItem;
+class LLFolderViewFilter;
+class LLPanel;
+class LLLineEditor;
+class LLMenuGL;
+class LLUICtrl;
+class LLTextBox;
+
+/**
+ * Class LLFolderViewScrollContainer
+ *
+ * A scroll container which provides the information about the height
+ * of currently displayed folder view contents.
+ * Used for updating vertical scroll bar visibility in inventory panel.
+ * See LLScrollContainer::calcVisibleSize().
+ */
+class LLFolderViewScrollContainer : public LLScrollContainer
+{
+public:
+ /*virtual*/ ~LLFolderViewScrollContainer() {};
+ /*virtual*/ const LLRect getScrolledViewRect() const;
+
+protected:
+ LLFolderViewScrollContainer(const LLScrollContainer::Params& p);
+ friend class LLUICtrlFactory;
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLFolderView
+//
+// The LLFolderView represents the root level folder view object.
+// It manages the screen region of the folder view.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLFolderView : public LLFolderViewFolder, public LLEditMenuHandler
+{
+public:
+ struct Params : public LLInitParam::Block<Params, LLFolderViewFolder::Params>
+ {
+ Mandatory<LLPanel*> parent_panel;
+ Optional<std::string> title;
+ Optional<bool> use_label_suffix,
+ allow_multiselect,
+ show_empty_message,
+ use_ellipses,
+ show_item_link_overlays;
+ Mandatory<LLFolderViewModelInterface*> view_model;
+ Optional<LLFolderViewGroupedItemModel*> grouped_item_model;
+ Mandatory<std::string> options_menu;
+
+
+ Params();
+ };
+
+ friend class LLFolderViewScrollContainer;
+ typedef folder_view_item_deque selected_items_t;
+
+ LLFolderView(const Params&);
+ virtual ~LLFolderView( void );
+
+ virtual BOOL canFocusChildren() const;
+
+ virtual const LLFolderView* getRoot() const { return this; }
+ virtual LLFolderView* getRoot() { return this; }
+
+ LLFolderViewModelInterface* getFolderViewModel() { return mViewModel; }
+ const LLFolderViewModelInterface* getFolderViewModel() const { return mViewModel; }
+
+ LLFolderViewGroupedItemModel* getFolderViewGroupedItemModel() { return mGroupedItemModel; }
+ const LLFolderViewGroupedItemModel* getFolderViewGroupedItemModel() const { return mGroupedItemModel; }
+
+ typedef boost::signals2::signal<void (const std::deque<LLFolderViewItem*>& items, BOOL user_action)> signal_t;
+ void setSelectCallback(const signal_t::slot_type& cb) { mSelectSignal.connect(cb); }
+ void setReshapeCallback(const signal_t::slot_type& cb) { mReshapeSignal.connect(cb); }
+
+ bool getAllowMultiSelect() { return mAllowMultiSelect; }
+
+ // Close all folders in the view
+ void closeAllFolders();
+ void openTopLevelFolders();
+
+ virtual void addFolder( LLFolderViewFolder* folder);
+
+ // Find width and height of this object and its children. Also
+ // makes sure that this view and its children are the right size.
+ virtual S32 arrange( S32* width, S32* height );
+ virtual S32 getItemHeight();
+
+ void arrangeAll() { mArrangeGeneration++; }
+ S32 getArrangeGeneration() { return mArrangeGeneration; }
+
+ // applies filters to control visibility of items
+ virtual void filter( LLFolderViewFilter& filter);
+
+ // Get the last selected item
+ virtual LLFolderViewItem* getCurSelectedItem( void );
+ selected_items_t& getSelectedItems( void );
+
+ // Record the selected item and pass it down the hierarchy.
+ virtual BOOL setSelection(LLFolderViewItem* selection, BOOL openitem,
+ BOOL take_keyboard_focus = TRUE);
+
+ // This method is used to toggle the selection of an item. Walks
+ // children, and keeps track of selected objects.
+ virtual BOOL changeSelection(LLFolderViewItem* selection, BOOL selected);
+
+ virtual std::set<LLFolderViewItem*> getSelectionList() const;
+
+ // Make sure if ancestor is selected, descendants are not
+ void sanitizeSelection();
+ virtual void clearSelection();
+ void addToSelectionList(LLFolderViewItem* item);
+ void removeFromSelectionList(LLFolderViewItem* item);
+
+ bool startDrag();
+ void setDragAndDropThisFrame() { mDragAndDropThisFrame = TRUE; }
+ void setDraggingOverItem(LLFolderViewItem* item) { mDraggingOverItem = item; }
+ LLFolderViewItem* getDraggingOverItem() { return mDraggingOverItem; }
+
+ // Deletion functionality
+ void removeSelectedItems();
+
+ void autoOpenItem(LLFolderViewFolder* item);
+ void closeAutoOpenedFolders();
+ BOOL autoOpenTest(LLFolderViewFolder* item);
+ BOOL isOpen() const { return TRUE; } // root folder always open
+
+ // Copy & paste
+ virtual BOOL canCopy() const;
+ virtual void copy();
+
+ virtual BOOL canCut() const;
+ virtual void cut();
+
+ virtual BOOL canPaste() const;
+ virtual void paste();
+
+ LLFolderViewItem* getNextUnselectedItem();
+
+ // Public rename functionality - can only start the process
+ void startRenamingSelectedItem( void );
+
+ // LLView functionality
+ ///*virtual*/ BOOL handleKey( KEY key, MASK mask, BOOL called_from_parent );
+ /*virtual*/ BOOL handleKeyHere( KEY key, MASK mask );
+ /*virtual*/ BOOL handleUnicodeCharHere(llwchar uni_char);
+ /*virtual*/ BOOL handleMouseDown( S32 x, S32 y, MASK mask );
+ /*virtual*/ BOOL handleDoubleClick( S32 x, S32 y, MASK mask );
+ /*virtual*/ BOOL handleRightMouseDown( S32 x, S32 y, MASK mask );
+ /*virtual*/ BOOL handleHover( S32 x, S32 y, MASK mask );
+ /*virtual*/ BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ std::string& tooltip_msg);
+ /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
+ /*virtual*/ void onMouseLeave(S32 x, S32 y, MASK mask) { setShowSelectionContext(FALSE); }
+ virtual void draw();
+ virtual void deleteAllChildren();
+
+ void scrollToShowSelection();
+ void scrollToShowItem(LLFolderViewItem* item, const LLRect& constraint_rect);
+ void setScrollContainer( LLScrollContainer* parent ) { mScrollContainer = parent; }
+ LLRect getVisibleRect();
+
+ BOOL search(LLFolderViewItem* first_item, const std::string &search_string, BOOL backward);
+ void setShowSelectionContext(bool show) { mShowSelectionContext = show; }
+ BOOL getShowSelectionContext();
+ void setShowSingleSelection(bool show);
+ BOOL getShowSingleSelection() { return mShowSingleSelection; }
+ F32 getSelectionFadeElapsedTime() { return mMultiSelectionFadeTimer.getElapsedTimeF32(); }
+ bool getUseEllipses() { return mUseEllipses; }
+ S32 getSelectedCount() { return (S32)mSelectedItems.size(); }
+
+ void update(); // needs to be called periodically (e.g. once per frame)
+
+ BOOL needsAutoSelect() { return mNeedsAutoSelect && !mAutoSelectOverride; }
+ BOOL needsAutoRename() { return mNeedsAutoRename; }
+ void setNeedsAutoRename(BOOL val) { mNeedsAutoRename = val; }
+ void setPinningSelectedItem(BOOL val) { mPinningSelectedItem = val; }
+ void setAutoSelectOverride(BOOL val) { mAutoSelectOverride = val; }
+
+ bool showItemLinkOverlays() { return mShowItemLinkOverlays; }
+
+ void setCallbackRegistrar(LLUICtrl::CommitCallbackRegistry::ScopedRegistrar* registrar) { mCallbackRegistrar = registrar; }
+
+ LLPanel* getParentPanel() { return mParentPanel.get(); }
+ // DEBUG only
+ void dumpSelectionInformation();
+
+ virtual S32 notify(const LLSD& info) ;
+
+ bool useLabelSuffix() { return mUseLabelSuffix; }
+ virtual void updateMenu();
+
+ // Note: We may eventually have to move that method up the hierarchy to LLFolderViewItem.
+ LLHandle<LLFolderView> getHandle() const { return getDerivedHandle<LLFolderView>(); }
+
+private:
+ void updateMenuOptions(LLMenuGL* menu);
+ void updateRenamerPosition();
+
+protected:
+ LLScrollContainer* mScrollContainer; // NULL if this is not a child of a scroll container.
+
+ void commitRename( const LLSD& data );
+ void onRenamerLost();
+
+ void finishRenamingItem( void );
+ void closeRenamer( void );
+
+ bool selectFirstItem();
+ bool selectLastItem();
+
+ BOOL addNoOptions(LLMenuGL* menu) const;
+
+
+protected:
+ LLHandle<LLView> mPopupMenuHandle;
+
+ selected_items_t mSelectedItems;
+ bool mKeyboardSelection,
+ mAllowMultiSelect,
+ mShowEmptyMessage,
+ mShowFolderHierarchy,
+ mNeedsScroll,
+ mPinningSelectedItem,
+ mNeedsAutoSelect,
+ mAutoSelectOverride,
+ mNeedsAutoRename,
+ mUseLabelSuffix,
+ mDragAndDropThisFrame,
+ mShowItemLinkOverlays,
+ mShowSelectionContext,
+ mShowSingleSelection;
+
+ // Renaming variables and methods
+ LLFolderViewItem* mRenameItem; // The item currently being renamed
+ LLLineEditor* mRenamer;
+
+ LLRect mScrollConstraintRect;
+
+ LLDepthStack<LLFolderViewFolder> mAutoOpenItems;
+ LLFolderViewFolder* mAutoOpenCandidate;
+ LLFrameTimer mAutoOpenTimer;
+ LLFrameTimer mSearchTimer;
+ std::string mSearchString;
+ LLFrameTimer mMultiSelectionFadeTimer;
+ S32 mArrangeGeneration;
+
+ signal_t mSelectSignal;
+ signal_t mReshapeSignal;
+ S32 mSignalSelectCallback;
+ S32 mMinWidth;
+
+ LLHandle<LLPanel> mParentPanel;
+
+ LLFolderViewModelInterface* mViewModel;
+ LLFolderViewGroupedItemModel* mGroupedItemModel;
+
+ /**
+ * Is used to determine if we need to cut text In LLFolderViewItem to avoid horizontal scroll.
+ * NOTE: For now it's used only to cut LLFolderViewItem::mLabel text for Landmarks in Places Panel.
+ */
+ bool mUseEllipses; // See EXT-719
+
+ /**
+ * Contains item under mouse pointer while dragging
+ */
+ LLFolderViewItem* mDraggingOverItem; // See EXT-719
+
+ LLUICtrl::CommitCallbackRegistry::ScopedRegistrar* mCallbackRegistrar;
+
+public:
+ static F32 sAutoOpenTime;
+ LLTextBox* mStatusTextBox;
+
+};
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLFolderViewFunctor
+//
+// Simple abstract base class for applying a functor to folders and
+// items in a folder view hierarchy. This is suboptimal for algorithms
+// that only work folders or only work on items, but I'll worry about
+// that later when it's determined to be too slow.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class LLFolderViewFunctor
+{
+public:
+ virtual ~LLFolderViewFunctor() {}
+ virtual void doFolder(LLFolderViewFolder* folder) = 0;
+ virtual void doItem(LLFolderViewItem* item) = 0;
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLSelectFirstFilteredItem
+//
+// This will select the first *item* found in the hierarchy. If no item can be
+// selected, the first matching folder will.
+// Since doFolder() is done first but we prioritize item selection, we let the
+// first filtered folder set the selection and raise a folder flag.
+// The selection might be overridden by the first filtered item in doItem()
+// which checks an item flag. Since doFolder() checks the item flag too, the first
+// item will still be selected if items were to be done first and folders second.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class LLSelectFirstFilteredItem : public LLFolderViewFunctor
+{
+public:
+ LLSelectFirstFilteredItem() : mItemSelected(FALSE), mFolderSelected(FALSE) {}
+ virtual ~LLSelectFirstFilteredItem() {}
+ virtual void doFolder(LLFolderViewFolder* folder);
+ virtual void doItem(LLFolderViewItem* item);
+ BOOL wasItemSelected() { return mItemSelected || mFolderSelected; }
+protected:
+ BOOL mItemSelected;
+ BOOL mFolderSelected;
+};
+
+class LLOpenFilteredFolders : public LLFolderViewFunctor
+{
+public:
+ LLOpenFilteredFolders() {}
+ virtual ~LLOpenFilteredFolders() {}
+ virtual void doFolder(LLFolderViewFolder* folder);
+ virtual void doItem(LLFolderViewItem* item);
+};
+
+class LLSaveFolderState : public LLFolderViewFunctor
+{
+public:
+ LLSaveFolderState() : mApply(FALSE) {}
+ virtual ~LLSaveFolderState() {}
+ virtual void doFolder(LLFolderViewFolder* folder);
+ virtual void doItem(LLFolderViewItem* item) {}
+ void setApply(BOOL apply);
+ void clearOpenFolders() { mOpenFolders.clear(); }
+protected:
+ std::set<LLUUID> mOpenFolders;
+ BOOL mApply;
+};
+
+class LLOpenFoldersWithSelection : public LLFolderViewFunctor
+{
+public:
+ LLOpenFoldersWithSelection() {}
+ virtual ~LLOpenFoldersWithSelection() {}
+ virtual void doFolder(LLFolderViewFolder* folder);
+ virtual void doItem(LLFolderViewItem* item);
+};
+
+// Flags for buildContextMenu()
+const U32 SUPPRESS_OPEN_ITEM = 0x1;
+const U32 FIRST_SELECTED_ITEM = 0x2;
+const U32 ITEM_IN_MULTI_SELECTION = 0x4;
+
+#endif // LL_LLFOLDERVIEW_H
diff --git a/indra/llui/llfolderviewitem.cpp b/indra/llui/llfolderviewitem.cpp
new file mode 100644
index 0000000000..747b472ac2
--- /dev/null
+++ b/indra/llui/llfolderviewitem.cpp
@@ -0,0 +1,2126 @@
+/**
+* @file llfolderviewitem.cpp
+* @brief Items and folders that can appear in a hierarchical folder view
+*
+* $LicenseInfo:firstyear=2001&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2010, Linden Research, Inc.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation;
+* version 2.1 of the License only.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+* $/LicenseInfo$
+*/
+#include "../newview/llviewerprecompiledheaders.h"
+
+#include "llflashtimer.h"
+
+#include "linden_common.h"
+#include "llfolderviewitem.h"
+#include "llfolderview.h"
+#include "llfolderviewmodel.h"
+#include "llpanel.h"
+#include "llcriticaldamp.h"
+#include "llclipboard.h"
+#include "llfocusmgr.h" // gFocusMgr
+#include "lltrans.h"
+#include "llwindow.h"
+
+///----------------------------------------------------------------------------
+/// Class LLFolderViewItem
+///----------------------------------------------------------------------------
+
+static LLDefaultChildRegistry::Register<LLFolderViewItem> r("folder_view_item");
+
+// statics
+std::map<U8, LLFontGL*> LLFolderViewItem::sFonts; // map of styles to fonts
+
+bool LLFolderViewItem::sColorSetInitialized = false;
+LLUIColor LLFolderViewItem::sFgColor;
+LLUIColor LLFolderViewItem::sHighlightBgColor;
+LLUIColor LLFolderViewItem::sFlashBgColor;
+LLUIColor LLFolderViewItem::sFocusOutlineColor;
+LLUIColor LLFolderViewItem::sMouseOverColor;
+LLUIColor LLFolderViewItem::sFilterBGColor;
+LLUIColor LLFolderViewItem::sFilterTextColor;
+LLUIColor LLFolderViewItem::sSuffixColor;
+LLUIColor LLFolderViewItem::sSearchStatusColor;
+
+// only integers can be initialized in header
+const F32 LLFolderViewItem::FOLDER_CLOSE_TIME_CONSTANT = 0.02f;
+const F32 LLFolderViewItem::FOLDER_OPEN_TIME_CONSTANT = 0.03f;
+
+const LLColor4U DEFAULT_WHITE(255, 255, 255);
+
+
+//static
+LLFontGL* LLFolderViewItem::getLabelFontForStyle(U8 style)
+{
+ LLFontGL* rtn = sFonts[style];
+ if (!rtn) // grab label font with this style, lazily
+ {
+ LLFontDescriptor labelfontdesc("SansSerif", "Small", style);
+ rtn = LLFontGL::getFont(labelfontdesc);
+ if (!rtn)
+ {
+ rtn = LLFontGL::getFontDefault();
+ }
+ sFonts[style] = rtn;
+ }
+ return rtn;
+}
+
+//static
+void LLFolderViewItem::initClass()
+{
+}
+
+//static
+void LLFolderViewItem::cleanupClass()
+{
+ sFonts.clear();
+}
+
+
+// NOTE: Optimize this, we call it a *lot* when opening a large inventory
+LLFolderViewItem::Params::Params()
+: root(),
+ listener(),
+ folder_arrow_image("folder_arrow_image"),
+ folder_indentation("folder_indentation"),
+ selection_image("selection_image"),
+ item_height("item_height"),
+ item_top_pad("item_top_pad"),
+ creation_date(),
+ allow_open("allow_open", true),
+ font_color("font_color"),
+ font_highlight_color("font_highlight_color"),
+ left_pad("left_pad", 0),
+ icon_pad("icon_pad", 0),
+ icon_width("icon_width", 0),
+ text_pad("text_pad", 0),
+ text_pad_right("text_pad_right", 0),
+ arrow_size("arrow_size", 0),
+ max_folder_item_overlap("max_folder_item_overlap", 0)
+{ }
+
+// Default constructor
+LLFolderViewItem::LLFolderViewItem(const LLFolderViewItem::Params& p)
+: LLView(p),
+ mLabelWidth(0),
+ mLabelWidthDirty(false),
+ mLabelPaddingRight(DEFAULT_LABEL_PADDING_RIGHT),
+ mParentFolder( NULL ),
+ mIsSelected( FALSE ),
+ mIsCurSelection( FALSE ),
+ mSelectPending(FALSE),
+ mLabelStyle( LLFontGL::NORMAL ),
+ mHasVisibleChildren(FALSE),
+ mLocalIndentation(p.folder_indentation),
+ mIndentation(0),
+ mItemHeight(p.item_height),
+ mControlLabelRotation(0.f),
+ mDragAndDropTarget(FALSE),
+ mLabel(p.name),
+ mRoot(p.root),
+ mViewModelItem(p.listener),
+ mIsMouseOverTitle(false),
+ mAllowOpen(p.allow_open),
+ mFontColor(p.font_color),
+ mFontHighlightColor(p.font_highlight_color),
+ mLeftPad(p.left_pad),
+ mIconPad(p.icon_pad),
+ mIconWidth(p.icon_width),
+ mTextPad(p.text_pad),
+ mTextPadRight(p.text_pad_right),
+ mArrowSize(p.arrow_size),
+ mMaxFolderItemOverlap(p.max_folder_item_overlap)
+{
+ if (!sColorSetInitialized)
+ {
+ sFgColor = LLUIColorTable::instance().getColor("MenuItemEnabledColor", DEFAULT_WHITE);
+ sHighlightBgColor = LLUIColorTable::instance().getColor("MenuItemHighlightBgColor", DEFAULT_WHITE);
+ sFlashBgColor = LLUIColorTable::instance().getColor("MenuItemFlashBgColor", DEFAULT_WHITE);
+ sFocusOutlineColor = LLUIColorTable::instance().getColor("InventoryFocusOutlineColor", DEFAULT_WHITE);
+ sMouseOverColor = LLUIColorTable::instance().getColor("InventoryMouseOverColor", DEFAULT_WHITE);
+ sFilterBGColor = LLUIColorTable::instance().getColor("FilterBackgroundColor", DEFAULT_WHITE);
+ sFilterTextColor = LLUIColorTable::instance().getColor("FilterTextColor", DEFAULT_WHITE);
+ sSuffixColor = LLUIColorTable::instance().getColor("InventoryItemColor", DEFAULT_WHITE);
+ sSearchStatusColor = LLUIColorTable::instance().getColor("InventorySearchStatusColor", DEFAULT_WHITE);
+ sColorSetInitialized = true;
+ }
+
+ if (mViewModelItem)
+ {
+ mViewModelItem->setFolderViewItem(this);
+ }
+}
+
+// Destroys the object
+LLFolderViewItem::~LLFolderViewItem()
+{
+ mViewModelItem = NULL;
+}
+
+BOOL LLFolderViewItem::postBuild()
+{
+ refresh();
+ return TRUE;
+}
+
+
+LLFolderView* LLFolderViewItem::getRoot()
+{
+ return mRoot;
+}
+
+const LLFolderView* LLFolderViewItem::getRoot() const
+{
+ return mRoot;
+}
+// Returns true if this object is a child (or grandchild, etc.) of potential_ancestor.
+BOOL LLFolderViewItem::isDescendantOf( const LLFolderViewFolder* potential_ancestor )
+{
+ LLFolderViewItem* root = this;
+ while( root->mParentFolder )
+ {
+ if( root->mParentFolder == potential_ancestor )
+ {
+ return TRUE;
+ }
+ root = root->mParentFolder;
+ }
+ return FALSE;
+}
+
+LLFolderViewItem* LLFolderViewItem::getNextOpenNode(BOOL include_children)
+{
+ if (!mParentFolder)
+ {
+ return NULL;
+ }
+
+ LLFolderViewItem* itemp = mParentFolder->getNextFromChild( this, include_children );
+ while(itemp && !itemp->getVisible())
+ {
+ LLFolderViewItem* next_itemp = itemp->mParentFolder->getNextFromChild( itemp, include_children );
+ if (itemp == next_itemp)
+ {
+ // hit last item
+ return itemp->getVisible() ? itemp : this;
+ }
+ itemp = next_itemp;
+ }
+
+ return itemp;
+}
+
+LLFolderViewItem* LLFolderViewItem::getPreviousOpenNode(BOOL include_children)
+{
+ if (!mParentFolder)
+ {
+ return NULL;
+ }
+
+ LLFolderViewItem* itemp = mParentFolder->getPreviousFromChild( this, include_children );
+
+ // Skip over items that are invisible or are hidden from the UI.
+ while(itemp && !itemp->getVisible())
+ {
+ LLFolderViewItem* next_itemp = itemp->mParentFolder->getPreviousFromChild( itemp, include_children );
+ if (itemp == next_itemp)
+ {
+ // hit first item
+ return itemp->getVisible() ? itemp : this;
+ }
+ itemp = next_itemp;
+ }
+
+ return itemp;
+}
+
+BOOL LLFolderViewItem::passedFilter(S32 filter_generation)
+{
+ return getViewModelItem()->passedFilter(filter_generation);
+}
+
+BOOL LLFolderViewItem::isPotentiallyVisible(S32 filter_generation)
+{
+ if (filter_generation < 0)
+ {
+ filter_generation = getFolderViewModel()->getFilter().getFirstSuccessGeneration();
+ }
+ LLFolderViewModelItem* model = getViewModelItem();
+ BOOL visible = model->passedFilter(filter_generation);
+ if (model->getMarkedDirtyGeneration() >= filter_generation)
+ {
+ // unsure visibility state
+ // retaining previous visibility until item is updated or filter generation changes
+ visible |= getVisible();
+ }
+ return visible;
+}
+
+void LLFolderViewItem::refresh()
+{
+ LLFolderViewModelItem& vmi = *getViewModelItem();
+
+ mLabel = vmi.getDisplayName();
+
+ setToolTip(vmi.getName());
+ mIcon = vmi.getIcon();
+ mIconOpen = vmi.getIconOpen();
+ mIconOverlay = vmi.getIconOverlay();
+
+ if (mRoot->useLabelSuffix())
+ {
+ mLabelStyle = vmi.getLabelStyle();
+ mLabelSuffix = vmi.getLabelSuffix();
+ }
+
+ mLabelWidthDirty = true;
+ // Dirty the filter flag of the model from the view (CHUI-849)
+ vmi.dirtyFilter();
+}
+
+// Utility function for LLFolderView
+void LLFolderViewItem::arrangeAndSet(BOOL set_selection,
+ BOOL take_keyboard_focus)
+{
+ LLFolderView* root = getRoot();
+ if (getParentFolder())
+ {
+ getParentFolder()->requestArrange();
+ }
+ if(set_selection)
+ {
+ getRoot()->setSelection(this, TRUE, take_keyboard_focus);
+ if(root)
+ {
+ root->scrollToShowSelection();
+ }
+ }
+}
+
+
+std::set<LLFolderViewItem*> LLFolderViewItem::getSelectionList() const
+{
+ std::set<LLFolderViewItem*> selection;
+ return selection;
+}
+
+// addToFolder() returns TRUE if it succeeds. FALSE otherwise
+void LLFolderViewItem::addToFolder(LLFolderViewFolder* folder)
+{
+ folder->addItem(this);
+
+ // Compute indentation since parent folder changed
+ mIndentation = (getParentFolder())
+ ? getParentFolder()->getIndentation() + mLocalIndentation
+ : 0;
+}
+
+
+// Finds width and height of this object and its children. Also
+// makes sure that this view and its children are the right size.
+S32 LLFolderViewItem::arrange( S32* width, S32* height )
+{
+ // Only indent deeper items in hierarchy
+ mIndentation = (getParentFolder())
+ ? getParentFolder()->getIndentation() + mLocalIndentation
+ : 0;
+ if (mLabelWidthDirty)
+ {
+ mLabelWidth = getLabelXPos() + getLabelFontForStyle(mLabelStyle)->getWidth(mLabel) + getLabelFontForStyle(mLabelStyle)->getWidth(mLabelSuffix) + mLabelPaddingRight;
+ mLabelWidthDirty = false;
+ }
+
+ *width = llmax(*width, mLabelWidth);
+
+ // determine if we need to use ellipses to avoid horizontal scroll. EXT-719
+ bool use_ellipses = getRoot()->getUseEllipses();
+ if (use_ellipses)
+ {
+ // limit to set rect to avoid horizontal scrollbar
+ *width = llmin(*width, getRoot()->getRect().getWidth());
+ }
+ *height = getItemHeight();
+ return *height;
+}
+
+S32 LLFolderViewItem::getItemHeight()
+{
+ return mItemHeight;
+}
+
+S32 LLFolderViewItem::getLabelXPos()
+{
+ return getIndentation() + mArrowSize + mTextPad + mIconWidth + mIconPad;
+}
+
+S32 LLFolderViewItem::getIconPad()
+{
+ return mIconPad;
+}
+
+S32 LLFolderViewItem::getTextPad()
+{
+ return mTextPad;
+}
+
+// *TODO: This can be optimized a lot by simply recording that it is
+// selected in the appropriate places, and assuming that set selection
+// means 'deselect' for a leaf item. Do this optimization after
+// multiple selection is implemented to make sure it all plays nice
+// together.
+BOOL LLFolderViewItem::setSelection(LLFolderViewItem* selection, BOOL openitem, BOOL take_keyboard_focus)
+{
+ if (selection == this && !mIsSelected)
+ {
+ selectItem();
+ }
+ else if (mIsSelected) // Deselect everything else.
+ {
+ deselectItem();
+ }
+ return mIsSelected;
+}
+
+BOOL LLFolderViewItem::changeSelection(LLFolderViewItem* selection, BOOL selected)
+{
+ if (selection == this)
+ {
+ if (mIsSelected)
+ {
+ deselectItem();
+ }
+ else
+ {
+ selectItem();
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void LLFolderViewItem::deselectItem(void)
+{
+ mIsSelected = FALSE;
+}
+
+void LLFolderViewItem::selectItem(void)
+{
+ if (mIsSelected == FALSE)
+ {
+ mIsSelected = TRUE;
+ getViewModelItem()->selectItem();
+ }
+}
+
+BOOL LLFolderViewItem::isMovable()
+{
+ return getViewModelItem()->isItemMovable();
+}
+
+BOOL LLFolderViewItem::isRemovable()
+{
+ return getViewModelItem()->isItemRemovable();
+}
+
+void LLFolderViewItem::destroyView()
+{
+ getRoot()->removeFromSelectionList(this);
+
+ if (mParentFolder)
+ {
+ // removeView deletes me
+ mParentFolder->extractItem(this);
+ }
+ delete this;
+}
+
+// Call through to the viewed object and return true if it can be
+// removed.
+//BOOL LLFolderViewItem::removeRecursively(BOOL single_item)
+BOOL LLFolderViewItem::remove()
+{
+ if(!isRemovable())
+ {
+ return FALSE;
+ }
+ return getViewModelItem()->removeItem();
+}
+
+// Build an appropriate context menu for the item.
+void LLFolderViewItem::buildContextMenu(LLMenuGL& menu, U32 flags)
+{
+ getViewModelItem()->buildContextMenu(menu, flags);
+}
+
+void LLFolderViewItem::openItem( void )
+{
+ if (mAllowOpen)
+ {
+ getViewModelItem()->openItem();
+ }
+}
+
+void LLFolderViewItem::rename(const std::string& new_name)
+{
+ if( !new_name.empty() )
+ {
+ getViewModelItem()->renameItem(new_name);
+ }
+}
+
+const std::string& LLFolderViewItem::getName( void ) const
+{
+ static const std::string noName("");
+ return getViewModelItem() ? getViewModelItem()->getName() : noName;
+}
+
+// LLView functionality
+BOOL LLFolderViewItem::handleRightMouseDown( S32 x, S32 y, MASK mask )
+{
+ if(!mIsSelected)
+ {
+ getRoot()->setSelection(this, FALSE);
+ }
+ make_ui_sound("UISndClick");
+ return TRUE;
+}
+
+BOOL LLFolderViewItem::handleMouseDown( S32 x, S32 y, MASK mask )
+{
+ if (LLView::childrenHandleMouseDown(x, y, mask))
+ {
+ return TRUE;
+ }
+
+ // No handler needed for focus lost since this class has no
+ // state that depends on it.
+ gFocusMgr.setMouseCapture( this );
+
+ if (!mIsSelected)
+ {
+ if(mask & MASK_CONTROL)
+ {
+ getRoot()->changeSelection(this, !mIsSelected);
+ }
+ else if (mask & MASK_SHIFT)
+ {
+ getParentFolder()->extendSelectionTo(this);
+ }
+ else
+ {
+ getRoot()->setSelection(this, FALSE);
+ }
+ make_ui_sound("UISndClick");
+ }
+ else
+ {
+ // If selected, we reserve the decision of deselecting/reselecting to the mouse up moment.
+ // This is necessary so we maintain selection consistent when starting a drag.
+ mSelectPending = TRUE;
+ }
+
+ mDragStartX = x;
+ mDragStartY = y;
+ return TRUE;
+}
+
+BOOL LLFolderViewItem::handleHover( S32 x, S32 y, MASK mask )
+{
+ static LLCachedControl<S32> drag_and_drop_threshold(*LLUI::sSettingGroups["config"],"DragAndDropDistanceThreshold", 3);
+
+ mIsMouseOverTitle = (y > (getRect().getHeight() - mItemHeight));
+
+ if( hasMouseCapture() && isMovable() )
+ {
+ LLFolderView* root = getRoot();
+
+ if( (x - mDragStartX) * (x - mDragStartX) + (y - mDragStartY) * (y - mDragStartY) > drag_and_drop_threshold() * drag_and_drop_threshold()
+ && root->getCurSelectedItem()
+ && root->startDrag())
+ {
+ // RN: when starting drag and drop, clear out last auto-open
+ root->autoOpenTest(NULL);
+ root->setShowSelectionContext(TRUE);
+
+ // Release keyboard focus, so that if stuff is dropped into the
+ // world, pressing the delete key won't blow away the inventory
+ // item.
+ gFocusMgr.setKeyboardFocus(NULL);
+
+ getWindow()->setCursor(UI_CURSOR_ARROW);
+ }
+ else if (x != mDragStartX || y != mDragStartY)
+ {
+ getWindow()->setCursor(UI_CURSOR_NOLOCKED);
+ }
+
+ return TRUE;
+ }
+ else
+ {
+ getRoot()->setShowSelectionContext(FALSE);
+ getWindow()->setCursor(UI_CURSOR_ARROW);
+ // let parent handle this then...
+ return FALSE;
+ }
+}
+
+
+BOOL LLFolderViewItem::handleDoubleClick( S32 x, S32 y, MASK mask )
+{
+ openItem();
+ return TRUE;
+}
+
+BOOL LLFolderViewItem::handleMouseUp( S32 x, S32 y, MASK mask )
+{
+ if (LLView::childrenHandleMouseUp(x, y, mask))
+ {
+ return TRUE;
+ }
+
+ // if mouse hasn't moved since mouse down...
+ if ( pointInView(x, y) && mSelectPending )
+ {
+ //...then select
+ if(mask & MASK_CONTROL)
+ {
+ getRoot()->changeSelection(this, !mIsSelected);
+ }
+ else if (mask & MASK_SHIFT)
+ {
+ getParentFolder()->extendSelectionTo(this);
+ }
+ else
+ {
+ getRoot()->setSelection(this, FALSE);
+ }
+ }
+
+ mSelectPending = FALSE;
+
+ if( hasMouseCapture() )
+ {
+ if (getRoot())
+ {
+ getRoot()->setShowSelectionContext(FALSE);
+ }
+ gFocusMgr.setMouseCapture( NULL );
+ }
+ return TRUE;
+}
+
+void LLFolderViewItem::onMouseLeave(S32 x, S32 y, MASK mask)
+{
+ mIsMouseOverTitle = false;
+}
+
+BOOL LLFolderViewItem::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ std::string& tooltip_msg)
+{
+ BOOL handled = FALSE;
+ BOOL accepted = getViewModelItem()->dragOrDrop(mask,drop,cargo_type,cargo_data, tooltip_msg);
+ handled = accepted;
+ if (accepted)
+ {
+ mDragAndDropTarget = TRUE;
+ *accept = ACCEPT_YES_MULTI;
+ }
+ else
+ {
+ *accept = ACCEPT_NO;
+ }
+ if(mParentFolder && !handled)
+ {
+ // store this item to get it in LLFolderBridge::dragItemIntoFolder on drop event.
+ mRoot->setDraggingOverItem(this);
+ handled = mParentFolder->handleDragAndDropFromChild(mask,drop,cargo_type,cargo_data,accept,tooltip_msg);
+ mRoot->setDraggingOverItem(NULL);
+ }
+ if (handled)
+ {
+ LL_DEBUGS("UserInput") << "dragAndDrop handled by LLFolderViewItem" << LL_ENDL;
+ }
+
+ return handled;
+}
+
+void LLFolderViewItem::drawOpenFolderArrow(const Params& default_params, const LLUIColor& fg_color)
+{
+ //--------------------------------------------------------------------------------//
+ // Draw open folder arrow
+ //
+ const S32 TOP_PAD = default_params.item_top_pad;
+
+ if (hasVisibleChildren())
+ {
+ LLUIImage* arrow_image = default_params.folder_arrow_image;
+ gl_draw_scaled_rotated_image(
+ mIndentation, getRect().getHeight() - mArrowSize - mTextPad - TOP_PAD,
+ mArrowSize, mArrowSize, mControlLabelRotation, arrow_image->getImage(), fg_color);
+ }
+}
+
+/*virtual*/ bool LLFolderViewItem::isHighlightAllowed()
+{
+ return mIsSelected;
+}
+
+/*virtual*/ bool LLFolderViewItem::isHighlightActive()
+{
+ return mIsCurSelection;
+}
+
+void LLFolderViewItem::drawHighlight(const BOOL showContent, const BOOL hasKeyboardFocus, const LLUIColor &selectColor, const LLUIColor &flashColor,
+ const LLUIColor &focusOutlineColor, const LLUIColor &mouseOverColor)
+{
+ const S32 focus_top = getRect().getHeight();
+ const S32 focus_bottom = getRect().getHeight() - mItemHeight;
+ const bool folder_open = (getRect().getHeight() > mItemHeight + 4);
+ const S32 FOCUS_LEFT = 1;
+
+ // Determine which background color to use for highlighting
+ LLUIColor bgColor = (isFlashing() ? flashColor : selectColor);
+
+ //--------------------------------------------------------------------------------//
+ // Draw highlight for selected items
+ // Note: Always render "current" item or flashing item, only render other selected
+ // items if mShowSingleSelection is FALSE.
+ //
+ if (isHighlightAllowed())
+
+ {
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+
+ // Highlight for selected but not current items
+ if (!isHighlightActive() && !isFlashing())
+ {
+ LLColor4 bg_color = bgColor;
+ // do time-based fade of extra objects
+ F32 fade_time = (getRoot() ? getRoot()->getSelectionFadeElapsedTime() : 0.0f);
+ if (getRoot() && getRoot()->getShowSingleSelection())
+ {
+ // fading out
+ bg_color.mV[VALPHA] = clamp_rescale(fade_time, 0.f, 0.4f, bg_color.mV[VALPHA], 0.f);
+ }
+ else
+ {
+ // fading in
+ bg_color.mV[VALPHA] = clamp_rescale(fade_time, 0.f, 0.4f, 0.f, bg_color.mV[VALPHA]);
+ }
+ gl_rect_2d(FOCUS_LEFT,
+ focus_top,
+ getRect().getWidth() - 2,
+ focus_bottom,
+ bg_color, hasKeyboardFocus);
+ }
+
+ // Highlight for currently selected or flashing item
+ if (isHighlightActive())
+ {
+ // Background
+ gl_rect_2d(FOCUS_LEFT,
+ focus_top,
+ getRect().getWidth() - 2,
+ focus_bottom,
+ bgColor, hasKeyboardFocus);
+ // Outline
+ gl_rect_2d(FOCUS_LEFT,
+ focus_top,
+ getRect().getWidth() - 2,
+ focus_bottom,
+ focusOutlineColor, FALSE);
+ }
+
+ if (folder_open)
+ {
+ gl_rect_2d(FOCUS_LEFT,
+ focus_bottom + 1, // overlap with bottom edge of above rect
+ getRect().getWidth() - 2,
+ 0,
+ focusOutlineColor, FALSE);
+ if (showContent && !isFlashing())
+ {
+ gl_rect_2d(FOCUS_LEFT,
+ focus_bottom + 1,
+ getRect().getWidth() - 2,
+ 0,
+ bgColor, TRUE);
+ }
+ }
+ }
+ else if (mIsMouseOverTitle)
+ {
+ gl_rect_2d(FOCUS_LEFT,
+ focus_top,
+ getRect().getWidth() - 2,
+ focus_bottom,
+ mouseOverColor, FALSE);
+ }
+
+ //--------------------------------------------------------------------------------//
+ // Draw DragNDrop highlight
+ //
+ if (mDragAndDropTarget)
+ {
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+ gl_rect_2d(FOCUS_LEFT,
+ focus_top,
+ getRect().getWidth() - 2,
+ focus_bottom,
+ bgColor, FALSE);
+ if (folder_open)
+ {
+ gl_rect_2d(FOCUS_LEFT,
+ focus_bottom + 1, // overlap with bottom edge of above rect
+ getRect().getWidth() - 2,
+ 0,
+ bgColor, FALSE);
+ }
+ mDragAndDropTarget = FALSE;
+ }
+}
+
+void LLFolderViewItem::drawLabel(const LLFontGL * font, const F32 x, const F32 y, const LLColor4& color, F32 &right_x)
+{
+ //--------------------------------------------------------------------------------//
+ // Draw the actual label text
+ //
+ font->renderUTF8(mLabel, 0, x, y, color,
+ LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW,
+ S32_MAX, getRect().getWidth() - (S32) x - mLabelPaddingRight, &right_x, TRUE);
+}
+
+void LLFolderViewItem::draw()
+{
+ const BOOL show_context = (getRoot() ? getRoot()->getShowSelectionContext() : FALSE);
+ const BOOL filled = show_context || (getRoot() ? getRoot()->getParentPanel()->hasFocus() : FALSE); // If we have keyboard focus, draw selection filled
+
+ const Params& default_params = LLUICtrlFactory::getDefaultParams<LLFolderViewItem>();
+ const S32 TOP_PAD = default_params.item_top_pad;
+
+ const LLFontGL* font = getLabelFontForStyle(mLabelStyle);
+
+ getViewModelItem()->update();
+
+ drawOpenFolderArrow(default_params, sFgColor);
+
+ drawHighlight(show_context, filled, sHighlightBgColor, sFlashBgColor, sFocusOutlineColor, sMouseOverColor);
+
+ //--------------------------------------------------------------------------------//
+ // Draw open icon
+ //
+ const S32 icon_x = mIndentation + mArrowSize + mTextPad;
+ if (!mIconOpen.isNull() && (llabs(mControlLabelRotation) > 80)) // For open folders
+ {
+ mIconOpen->draw(icon_x, getRect().getHeight() - mIconOpen->getHeight() - TOP_PAD + 1);
+ }
+ else if (mIcon)
+ {
+ mIcon->draw(icon_x, getRect().getHeight() - mIcon->getHeight() - TOP_PAD + 1);
+ }
+
+ if (mIconOverlay && getRoot()->showItemLinkOverlays())
+ {
+ mIconOverlay->draw(icon_x, getRect().getHeight() - mIcon->getHeight() - TOP_PAD + 1);
+ }
+
+ //--------------------------------------------------------------------------------//
+ // Exit if no label to draw
+ //
+ if (mLabel.empty())
+ {
+ return;
+ }
+
+ std::string::size_type filter_string_length = mViewModelItem->hasFilterStringMatch() ? mViewModelItem->getFilterStringSize() : 0;
+ F32 right_x = 0;
+ F32 y = (F32)getRect().getHeight() - font->getLineHeight() - (F32)mTextPad - (F32)TOP_PAD;
+ F32 text_left = (F32)getLabelXPos();
+ std::string combined_string = mLabel + mLabelSuffix;
+
+ if (filter_string_length > 0)
+ {
+ S32 left = ll_round(text_left) + font->getWidth(combined_string, 0, mViewModelItem->getFilterStringOffset()) - 2;
+ S32 right = left + font->getWidth(combined_string, mViewModelItem->getFilterStringOffset(), filter_string_length) + 2;
+ S32 bottom = llfloor(getRect().getHeight() - font->getLineHeight() - 3 - TOP_PAD);
+ S32 top = getRect().getHeight() - TOP_PAD;
+
+ LLUIImage* box_image = default_params.selection_image;
+ LLRect box_rect(left, top, right, bottom);
+ box_image->draw(box_rect, sFilterBGColor);
+ }
+
+ LLColor4 color = (mIsSelected && filled) ? mFontHighlightColor : mFontColor;
+ drawLabel(font, text_left, y, color, right_x);
+
+ //--------------------------------------------------------------------------------//
+ // Draw label suffix
+ //
+ if (!mLabelSuffix.empty())
+ {
+ font->renderUTF8( mLabelSuffix, 0, right_x, y, sSuffixColor,
+ LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW,
+ S32_MAX, S32_MAX, &right_x, FALSE );
+ }
+
+ //--------------------------------------------------------------------------------//
+ // Highlight string match
+ //
+ if (filter_string_length > 0)
+ {
+ F32 match_string_left = text_left + font->getWidthF32(combined_string, 0, mViewModelItem->getFilterStringOffset());
+ F32 yy = (F32)getRect().getHeight() - font->getLineHeight() - (F32)mTextPad - (F32)TOP_PAD;
+ font->renderUTF8( combined_string, mViewModelItem->getFilterStringOffset(), match_string_left, yy,
+ sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW,
+ filter_string_length, S32_MAX, &right_x, FALSE );
+ }
+
+ //Gilbert Linden 9-20-2012: Although this should be legal, removing it because it causes the mLabelSuffix rendering to
+ //be distorted...oddly. I initially added this in but didn't need it after all. So removing to prevent unnecessary bug.
+ //LLView::draw();
+}
+
+const LLFolderViewModelInterface* LLFolderViewItem::getFolderViewModel( void ) const
+{
+ return getRoot()->getFolderViewModel();
+}
+
+LLFolderViewModelInterface* LLFolderViewItem::getFolderViewModel( void )
+{
+ return getRoot()->getFolderViewModel();
+}
+
+bool LLFolderViewItem::isInSelection() const
+{
+ return mIsSelected || (mParentFolder && mParentFolder->isInSelection());
+}
+
+
+
+///----------------------------------------------------------------------------
+/// Class LLFolderViewFolder
+///----------------------------------------------------------------------------
+
+LLFolderViewFolder::LLFolderViewFolder( const LLFolderViewItem::Params& p ):
+ LLFolderViewItem( p ),
+ mIsOpen(FALSE),
+ mExpanderHighlighted(FALSE),
+ mCurHeight(0.f),
+ mTargetHeight(0.f),
+ mAutoOpenCountdown(0.f),
+ mLastArrangeGeneration( -1 ),
+ mLastCalculatedWidth(0)
+{
+}
+
+void LLFolderViewFolder::updateLabelRotation()
+{
+ if (mAutoOpenCountdown != 0.f)
+ {
+ mControlLabelRotation = mAutoOpenCountdown * -90.f;
+ }
+ else if (isOpen())
+ {
+ mControlLabelRotation = lerp(mControlLabelRotation, -90.f, LLSmoothInterpolation::getInterpolant(0.04f));
+ }
+ else
+ {
+ mControlLabelRotation = lerp(mControlLabelRotation, 0.f, LLSmoothInterpolation::getInterpolant(0.025f));
+ }
+}
+
+// Destroys the object
+LLFolderViewFolder::~LLFolderViewFolder( void )
+{
+ // The LLView base class takes care of object destruction. make sure that we
+ // don't have mouse or keyboard focus
+ gFocusMgr.releaseFocusIfNeeded( this ); // calls onCommit()
+}
+
+// addToFolder() returns TRUE if it succeeds. FALSE otherwise
+void LLFolderViewFolder::addToFolder(LLFolderViewFolder* folder)
+{
+ folder->addFolder(this);
+
+ // Compute indentation since parent folder changed
+ mIndentation = (getParentFolder())
+ ? getParentFolder()->getIndentation() + mLocalIndentation
+ : 0;
+}
+
+static LLTrace::BlockTimerStatHandle FTM_ARRANGE("Arrange");
+
+// Make everything right and in the right place ready for drawing (CHUI-849)
+// * Sort everything correctly if necessary
+// * Turn widgets visible/invisible according to their model filtering state
+// * Takes animation state into account for opening/closing of folders (this makes widgets visible/invisible)
+// * Reposition visible widgets so that they line up correctly with no gap
+// * Compute the width and height of the current folder and its children
+// * Makes sure that this view and its children are the right size
+S32 LLFolderViewFolder::arrange( S32* width, S32* height )
+{
+ // Sort before laying out contents
+ // Note that we sort from the root (CHUI-849)
+ getRoot()->getFolderViewModel()->sort(this);
+
+ LL_RECORD_BLOCK_TIME(FTM_ARRANGE);
+
+ // evaluate mHasVisibleChildren
+ mHasVisibleChildren = false;
+ if (getViewModelItem()->descendantsPassedFilter())
+ {
+ // We have to verify that there's at least one child that's not filtered out
+ bool found = false;
+ // Try the items first
+ for (items_t::iterator iit = mItems.begin(); iit != mItems.end(); ++iit)
+ {
+ LLFolderViewItem* itemp = (*iit);
+ found = itemp->isPotentiallyVisible();
+ if (found)
+ break;
+ }
+ if (!found)
+ {
+ // If no item found, try the folders
+ for (folders_t::iterator fit = mFolders.begin(); fit != mFolders.end(); ++fit)
+ {
+ LLFolderViewFolder* folderp = (*fit);
+ found = folderp->isPotentiallyVisible();
+ if (found)
+ break;
+ }
+ }
+
+ mHasVisibleChildren = found;
+ }
+
+ // calculate height as a single item (without any children), and reshapes rectangle to match
+ LLFolderViewItem::arrange( width, height );
+
+ // clamp existing animated height so as to never get smaller than a single item
+ mCurHeight = llmax((F32)*height, mCurHeight);
+
+ // initialize running height value as height of single item in case we have no children
+ F32 running_height = (F32)*height;
+ F32 target_height = (F32)*height;
+
+ // are my children visible?
+ if (needsArrange())
+ {
+ // set last arrange generation first, in case children are animating
+ // and need to be arranged again
+ mLastArrangeGeneration = getRoot()->getArrangeGeneration();
+ if (isOpen())
+ {
+ // Add sizes of children
+ S32 parent_item_height = getRect().getHeight();
+
+ for(folders_t::iterator fit = mFolders.begin(); fit != mFolders.end(); ++fit)
+ {
+ LLFolderViewFolder* folderp = (*fit);
+ folderp->setVisible(folderp->isPotentiallyVisible());
+
+ if (folderp->getVisible())
+ {
+ S32 child_width = *width;
+ S32 child_height = 0;
+ S32 child_top = parent_item_height - ll_round(running_height);
+
+ target_height += folderp->arrange( &child_width, &child_height );
+
+ running_height += (F32)child_height;
+ *width = llmax(*width, child_width);
+ folderp->setOrigin( 0, child_top - folderp->getRect().getHeight() );
+ }
+ }
+ for(items_t::iterator iit = mItems.begin();
+ iit != mItems.end(); ++iit)
+ {
+ LLFolderViewItem* itemp = (*iit);
+ itemp->setVisible(itemp->isPotentiallyVisible());
+
+ if (itemp->getVisible())
+ {
+ S32 child_width = *width;
+ S32 child_height = 0;
+ S32 child_top = parent_item_height - ll_round(running_height);
+
+ target_height += itemp->arrange( &child_width, &child_height );
+ // don't change width, as this item is as wide as its parent folder by construction
+ itemp->reshape( itemp->getRect().getWidth(), child_height);
+
+ running_height += (F32)child_height;
+ *width = llmax(*width, child_width);
+ itemp->setOrigin( 0, child_top - itemp->getRect().getHeight() );
+ }
+ }
+ }
+
+ mTargetHeight = target_height;
+ // cache this width so next time we can just return it
+ mLastCalculatedWidth = *width;
+ }
+ else
+ {
+ // just use existing width
+ *width = mLastCalculatedWidth;
+ }
+
+ // animate current height towards target height
+ if (llabs(mCurHeight - mTargetHeight) > 1.f)
+ {
+ mCurHeight = lerp(mCurHeight, mTargetHeight, LLSmoothInterpolation::getInterpolant(isOpen() ? FOLDER_OPEN_TIME_CONSTANT : FOLDER_CLOSE_TIME_CONSTANT));
+
+ requestArrange();
+
+ // hide child elements that fall out of current animated height
+ for (folders_t::iterator iter = mFolders.begin();
+ iter != mFolders.end();)
+ {
+ folders_t::iterator fit = iter++;
+ // number of pixels that bottom of folder label is from top of parent folder
+ if (getRect().getHeight() - (*fit)->getRect().mTop + (*fit)->getItemHeight()
+ > ll_round(mCurHeight) + mMaxFolderItemOverlap)
+ {
+ // hide if beyond current folder height
+ (*fit)->setVisible(FALSE);
+ }
+ }
+
+ for (items_t::iterator iter = mItems.begin();
+ iter != mItems.end();)
+ {
+ items_t::iterator iit = iter++;
+ // number of pixels that bottom of item label is from top of parent folder
+ if (getRect().getHeight() - (*iit)->getRect().mBottom
+ > ll_round(mCurHeight) + mMaxFolderItemOverlap)
+ {
+ (*iit)->setVisible(FALSE);
+ }
+ }
+ }
+ else
+ {
+ mCurHeight = mTargetHeight;
+ }
+
+ // don't change width as this item is already as wide as its parent folder
+ reshape(getRect().getWidth(),ll_round(mCurHeight));
+
+ // pass current height value back to parent
+ *height = ll_round(mCurHeight);
+
+ return ll_round(mTargetHeight);
+}
+
+BOOL LLFolderViewFolder::needsArrange()
+{
+ return mLastArrangeGeneration < getRoot()->getArrangeGeneration();
+}
+
+// Passes selection information on to children and record selection
+// information if necessary.
+BOOL LLFolderViewFolder::setSelection(LLFolderViewItem* selection, BOOL openitem,
+ BOOL take_keyboard_focus)
+{
+ BOOL rv = FALSE;
+ if (selection == this)
+ {
+ if (!isSelected())
+ {
+ selectItem();
+ }
+ rv = TRUE;
+ }
+ else
+ {
+ if (isSelected())
+ {
+ deselectItem();
+ }
+ rv = FALSE;
+ }
+ BOOL child_selected = FALSE;
+
+ for (folders_t::iterator iter = mFolders.begin();
+ iter != mFolders.end();)
+ {
+ folders_t::iterator fit = iter++;
+ if((*fit)->setSelection(selection, openitem, take_keyboard_focus))
+ {
+ rv = TRUE;
+ child_selected = TRUE;
+ }
+ }
+ for (items_t::iterator iter = mItems.begin();
+ iter != mItems.end();)
+ {
+ items_t::iterator iit = iter++;
+ if((*iit)->setSelection(selection, openitem, take_keyboard_focus))
+ {
+ rv = TRUE;
+ child_selected = TRUE;
+ }
+ }
+ if(openitem && child_selected)
+ {
+ setOpenArrangeRecursively(TRUE);
+ }
+ return rv;
+}
+
+// This method is used to change the selection of an item.
+// Recursively traverse all children; if 'selection' is 'this' then change
+// the select status if necessary.
+// Returns TRUE if the selection state of this folder, or of a child, was changed.
+BOOL LLFolderViewFolder::changeSelection(LLFolderViewItem* selection, BOOL selected)
+{
+ BOOL rv = FALSE;
+ if(selection == this)
+ {
+ if (isSelected() != selected)
+ {
+ rv = TRUE;
+ if (selected)
+ {
+ selectItem();
+ }
+ else
+ {
+ deselectItem();
+ }
+ }
+ }
+
+ for (folders_t::iterator iter = mFolders.begin();
+ iter != mFolders.end();)
+ {
+ folders_t::iterator fit = iter++;
+ if((*fit)->changeSelection(selection, selected))
+ {
+ rv = TRUE;
+ }
+ }
+ for (items_t::iterator iter = mItems.begin();
+ iter != mItems.end();)
+ {
+ items_t::iterator iit = iter++;
+ if((*iit)->changeSelection(selection, selected))
+ {
+ rv = TRUE;
+ }
+ }
+ return rv;
+}
+
+LLFolderViewFolder* LLFolderViewFolder::getCommonAncestor(LLFolderViewItem* item_a, LLFolderViewItem* item_b, bool& reverse)
+{
+ if (!item_a->getParentFolder() || !item_b->getParentFolder()) return NULL;
+
+ std::deque<LLFolderViewFolder*> item_a_ancestors;
+
+ LLFolderViewFolder* parent = item_a->getParentFolder();
+ while(parent)
+ {
+ item_a_ancestors.push_back(parent);
+ parent = parent->getParentFolder();
+ }
+
+ std::deque<LLFolderViewFolder*> item_b_ancestors;
+
+ parent = item_b->getParentFolder();
+ while(parent)
+ {
+ item_b_ancestors.push_back(parent);
+ parent = parent->getParentFolder();
+ }
+
+ LLFolderViewFolder* common_ancestor = item_a->getRoot();
+
+ while(item_a_ancestors.size() > item_b_ancestors.size())
+ {
+ item_a = item_a_ancestors.front();
+ item_a_ancestors.pop_front();
+ }
+
+ while(item_b_ancestors.size() > item_a_ancestors.size())
+ {
+ item_b = item_b_ancestors.front();
+ item_b_ancestors.pop_front();
+ }
+
+ while(item_a_ancestors.size())
+ {
+ common_ancestor = item_a_ancestors.front();
+
+ if (item_a_ancestors.front() == item_b_ancestors.front())
+ {
+ // which came first, sibling a or sibling b?
+ for (folders_t::iterator it = common_ancestor->mFolders.begin(), end_it = common_ancestor->mFolders.end();
+ it != end_it;
+ ++it)
+ {
+ LLFolderViewItem* item = *it;
+
+ if (item == item_a)
+ {
+ reverse = false;
+ return common_ancestor;
+ }
+ if (item == item_b)
+ {
+ reverse = true;
+ return common_ancestor;
+ }
+ }
+
+ for (items_t::iterator it = common_ancestor->mItems.begin(), end_it = common_ancestor->mItems.end();
+ it != end_it;
+ ++it)
+ {
+ LLFolderViewItem* item = *it;
+
+ if (item == item_a)
+ {
+ reverse = false;
+ return common_ancestor;
+ }
+ if (item == item_b)
+ {
+ reverse = true;
+ return common_ancestor;
+ }
+ }
+ break;
+ }
+
+ item_a = item_a_ancestors.front();
+ item_a_ancestors.pop_front();
+ item_b = item_b_ancestors.front();
+ item_b_ancestors.pop_front();
+ }
+
+ return NULL;
+}
+
+void LLFolderViewFolder::gatherChildRangeExclusive(LLFolderViewItem* start, LLFolderViewItem* end, bool reverse, std::vector<LLFolderViewItem*>& items)
+{
+ bool selecting = start == NULL;
+ if (reverse)
+ {
+ for (items_t::reverse_iterator it = mItems.rbegin(), end_it = mItems.rend();
+ it != end_it;
+ ++it)
+ {
+ if (*it == end)
+ {
+ return;
+ }
+ if (selecting)
+ {
+ items.push_back(*it);
+ }
+
+ if (*it == start)
+ {
+ selecting = true;
+ }
+ }
+ for (folders_t::reverse_iterator it = mFolders.rbegin(), end_it = mFolders.rend();
+ it != end_it;
+ ++it)
+ {
+ if (*it == end)
+ {
+ return;
+ }
+
+ if (selecting)
+ {
+ items.push_back(*it);
+ }
+
+ if (*it == start)
+ {
+ selecting = true;
+ }
+ }
+ }
+ else
+ {
+ for (folders_t::iterator it = mFolders.begin(), end_it = mFolders.end();
+ it != end_it;
+ ++it)
+ {
+ if (*it == end)
+ {
+ return;
+ }
+
+ if (selecting)
+ {
+ items.push_back(*it);
+ }
+
+ if (*it == start)
+ {
+ selecting = true;
+ }
+ }
+ for (items_t::iterator it = mItems.begin(), end_it = mItems.end();
+ it != end_it;
+ ++it)
+ {
+ if (*it == end)
+ {
+ return;
+ }
+
+ if (selecting)
+ {
+ items.push_back(*it);
+ }
+
+ if (*it == start)
+ {
+ selecting = true;
+ }
+ }
+ }
+}
+
+void LLFolderViewFolder::extendSelectionTo(LLFolderViewItem* new_selection)
+{
+ if (getRoot()->getAllowMultiSelect() == FALSE) return;
+
+ LLFolderViewItem* cur_selected_item = getRoot()->getCurSelectedItem();
+ if (cur_selected_item == NULL)
+ {
+ cur_selected_item = new_selection;
+ }
+
+
+ bool reverse = false;
+ LLFolderViewFolder* common_ancestor = getCommonAncestor(cur_selected_item, new_selection, reverse);
+ if (!common_ancestor) return;
+
+ LLFolderViewItem* last_selected_item_from_cur = cur_selected_item;
+ LLFolderViewFolder* cur_folder = cur_selected_item->getParentFolder();
+
+ std::vector<LLFolderViewItem*> items_to_select_forward;
+
+ while(cur_folder != common_ancestor)
+ {
+ cur_folder->gatherChildRangeExclusive(last_selected_item_from_cur, NULL, reverse, items_to_select_forward);
+
+ last_selected_item_from_cur = cur_folder;
+ cur_folder = cur_folder->getParentFolder();
+ }
+
+ std::vector<LLFolderViewItem*> items_to_select_reverse;
+
+ LLFolderViewItem* last_selected_item_from_new = new_selection;
+ cur_folder = new_selection->getParentFolder();
+ while(cur_folder != common_ancestor)
+ {
+ cur_folder->gatherChildRangeExclusive(last_selected_item_from_new, NULL, !reverse, items_to_select_reverse);
+
+ last_selected_item_from_new = cur_folder;
+ cur_folder = cur_folder->getParentFolder();
+ }
+
+ common_ancestor->gatherChildRangeExclusive(last_selected_item_from_cur, last_selected_item_from_new, reverse, items_to_select_forward);
+
+ for (std::vector<LLFolderViewItem*>::reverse_iterator it = items_to_select_reverse.rbegin(), end_it = items_to_select_reverse.rend();
+ it != end_it;
+ ++it)
+ {
+ items_to_select_forward.push_back(*it);
+ }
+
+ LLFolderView* root = getRoot();
+
+ for (std::vector<LLFolderViewItem*>::iterator it = items_to_select_forward.begin(), end_it = items_to_select_forward.end();
+ it != end_it;
+ ++it)
+ {
+ LLFolderViewItem* item = *it;
+ if (item->isSelected())
+ {
+ root->removeFromSelectionList(item);
+ }
+ else
+ {
+ item->selectItem();
+ }
+ root->addToSelectionList(item);
+ }
+
+ if (new_selection->isSelected())
+ {
+ root->removeFromSelectionList(new_selection);
+ }
+ else
+ {
+ new_selection->selectItem();
+ }
+ root->addToSelectionList(new_selection);
+}
+
+
+void LLFolderViewFolder::destroyView()
+{
+ while (!mItems.empty())
+ {
+ LLFolderViewItem *itemp = mItems.back();
+ itemp->destroyView(); // LLFolderViewItem::destroyView() removes entry from mItems
+ }
+
+ while (!mFolders.empty())
+ {
+ LLFolderViewFolder *folderp = mFolders.back();
+ folderp->destroyView(); // LLFolderVievFolder::destroyView() removes entry from mFolders
+ }
+
+ LLFolderViewItem::destroyView();
+}
+
+// extractItem() removes the specified item from the folder, but
+// doesn't delete it.
+void LLFolderViewFolder::extractItem( LLFolderViewItem* item )
+{
+ if (item->isSelected())
+ getRoot()->clearSelection();
+ items_t::iterator it = std::find(mItems.begin(), mItems.end(), item);
+ if(it == mItems.end())
+ {
+ // This is an evil downcast. However, it's only doing
+ // pointer comparison to find if (which it should be ) the
+ // item is in the container, so it's pretty safe.
+ LLFolderViewFolder* f = static_cast<LLFolderViewFolder*>(item);
+ folders_t::iterator ft;
+ ft = std::find(mFolders.begin(), mFolders.end(), f);
+ if (ft != mFolders.end())
+ {
+ mFolders.erase(ft);
+ }
+ }
+ else
+ {
+ mItems.erase(it);
+ }
+ //item has been removed, need to update filter
+ getViewModelItem()->removeChild(item->getViewModelItem());
+ //because an item is going away regardless of filter status, force rearrange
+ requestArrange();
+ removeChild(item);
+}
+
+BOOL LLFolderViewFolder::isMovable()
+{
+ if( !(getViewModelItem()->isItemMovable()) )
+ {
+ return FALSE;
+ }
+
+ for (items_t::iterator iter = mItems.begin();
+ iter != mItems.end();)
+ {
+ items_t::iterator iit = iter++;
+ if(!(*iit)->isMovable())
+ {
+ return FALSE;
+ }
+ }
+
+ for (folders_t::iterator iter = mFolders.begin();
+ iter != mFolders.end();)
+ {
+ folders_t::iterator fit = iter++;
+ if(!(*fit)->isMovable())
+ {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+
+BOOL LLFolderViewFolder::isRemovable()
+{
+ if( !(getViewModelItem()->isItemRemovable()) )
+ {
+ return FALSE;
+ }
+
+ for (items_t::iterator iter = mItems.begin();
+ iter != mItems.end();)
+ {
+ items_t::iterator iit = iter++;
+ if(!(*iit)->isRemovable())
+ {
+ return FALSE;
+ }
+ }
+
+ for (folders_t::iterator iter = mFolders.begin();
+ iter != mFolders.end();)
+ {
+ folders_t::iterator fit = iter++;
+ if(!(*fit)->isRemovable())
+ {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+// this is an internal method used for adding items to folders.
+void LLFolderViewFolder::addItem(LLFolderViewItem* item)
+{
+ if (item->getParentFolder())
+ {
+ item->getParentFolder()->extractItem(item);
+ }
+ item->setParentFolder(this);
+
+ mItems.push_back(item);
+
+ item->setRect(LLRect(0, 0, getRect().getWidth(), 0));
+ item->setVisible(FALSE);
+
+ addChild(item);
+
+ // When the model is already hooked into a hierarchy (i.e. has a parent), do not reparent it
+ // Note: this happens when models are created before views or shared between views
+ if (!item->getViewModelItem()->hasParent())
+ {
+ getViewModelItem()->addChild(item->getViewModelItem());
+ }
+}
+
+// this is an internal method used for adding items to folders.
+void LLFolderViewFolder::addFolder(LLFolderViewFolder* folder)
+{
+ if (folder->mParentFolder)
+ {
+ folder->mParentFolder->extractItem(folder);
+ }
+ folder->mParentFolder = this;
+ mFolders.push_back(folder);
+ folder->setOrigin(0, 0);
+ folder->reshape(getRect().getWidth(), 0);
+ folder->setVisible(FALSE);
+ // rearrange all descendants too, as our indentation level might have changed
+ //folder->requestArrange();
+ //requestSort();
+
+ addChild(folder);
+
+ // When the model is already hooked into a hierarchy (i.e. has a parent), do not reparent it
+ // Note: this happens when models are created before views or shared between views
+ if (!folder->getViewModelItem()->hasParent())
+ {
+ getViewModelItem()->addChild(folder->getViewModelItem());
+ }
+}
+
+void LLFolderViewFolder::requestArrange()
+{
+ mLastArrangeGeneration = -1;
+ // flag all items up to root
+ if (mParentFolder)
+ {
+ mParentFolder->requestArrange();
+ }
+}
+
+void LLFolderViewFolder::toggleOpen()
+{
+ setOpen(!isOpen());
+}
+
+// Force a folder open or closed
+void LLFolderViewFolder::setOpen(BOOL openitem)
+{
+ setOpenArrangeRecursively(openitem);
+}
+
+void LLFolderViewFolder::setOpenArrangeRecursively(BOOL openitem, ERecurseType recurse)
+{
+ BOOL was_open = isOpen();
+ mIsOpen = openitem;
+ if(!was_open && openitem)
+ {
+ getViewModelItem()->openItem();
+ }
+ else if(was_open && !openitem)
+ {
+ getViewModelItem()->closeItem();
+ }
+
+ if (recurse == RECURSE_DOWN || recurse == RECURSE_UP_DOWN)
+ {
+ for (folders_t::iterator iter = mFolders.begin();
+ iter != mFolders.end();)
+ {
+ folders_t::iterator fit = iter++;
+ (*fit)->setOpenArrangeRecursively(openitem, RECURSE_DOWN); /* Flawfinder: ignore */
+ }
+ }
+ if (mParentFolder
+ && (recurse == RECURSE_UP
+ || recurse == RECURSE_UP_DOWN))
+ {
+ mParentFolder->setOpenArrangeRecursively(openitem, RECURSE_UP);
+ }
+
+ if (was_open != isOpen())
+ {
+ requestArrange();
+ }
+}
+
+BOOL LLFolderViewFolder::handleDragAndDropFromChild(MASK mask,
+ BOOL drop,
+ EDragAndDropType c_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ std::string& tooltip_msg)
+{
+ BOOL accepted = mViewModelItem->dragOrDrop(mask,drop,c_type,cargo_data, tooltip_msg);
+ if (accepted)
+ {
+ mDragAndDropTarget = TRUE;
+ *accept = ACCEPT_YES_MULTI;
+ }
+ else
+ {
+ *accept = ACCEPT_NO;
+ }
+
+ // drag and drop to child item, so clear pending auto-opens
+ getRoot()->autoOpenTest(NULL);
+
+ return TRUE;
+}
+
+void LLFolderViewFolder::openItem( void )
+{
+ toggleOpen();
+}
+
+void LLFolderViewFolder::applyFunctorToChildren(LLFolderViewFunctor& functor)
+{
+ for (folders_t::iterator iter = mFolders.begin();
+ iter != mFolders.end();)
+ {
+ folders_t::iterator fit = iter++;
+ functor.doItem((*fit));
+ }
+ for (items_t::iterator iter = mItems.begin();
+ iter != mItems.end();)
+ {
+ items_t::iterator iit = iter++;
+ functor.doItem((*iit));
+ }
+}
+
+void LLFolderViewFolder::applyFunctorRecursively(LLFolderViewFunctor& functor)
+{
+ functor.doFolder(this);
+
+ for (folders_t::iterator iter = mFolders.begin();
+ iter != mFolders.end();)
+ {
+ folders_t::iterator fit = iter++;
+ (*fit)->applyFunctorRecursively(functor);
+ }
+ for (items_t::iterator iter = mItems.begin();
+ iter != mItems.end();)
+ {
+ items_t::iterator iit = iter++;
+ functor.doItem((*iit));
+ }
+}
+
+// LLView functionality
+BOOL LLFolderViewFolder::handleDragAndDrop(S32 x, S32 y, MASK mask,
+ BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ std::string& tooltip_msg)
+{
+ BOOL handled = FALSE;
+
+ if (isOpen())
+ {
+ handled = (childrenHandleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg) != NULL);
+ }
+
+ if (!handled)
+ {
+ handleDragAndDropToThisFolder(mask, drop, cargo_type, cargo_data, accept, tooltip_msg);
+
+ LL_DEBUGS("UserInput") << "dragAndDrop handled by LLFolderViewFolder" << LL_ENDL;
+ }
+
+ return TRUE;
+}
+
+BOOL LLFolderViewFolder::handleDragAndDropToThisFolder(MASK mask,
+ BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ std::string& tooltip_msg)
+{
+ BOOL accepted = getViewModelItem()->dragOrDrop(mask,drop,cargo_type,cargo_data, tooltip_msg);
+
+ if (accepted)
+ {
+ mDragAndDropTarget = TRUE;
+ *accept = ACCEPT_YES_MULTI;
+ }
+ else
+ {
+ *accept = ACCEPT_NO;
+ }
+
+ if (!drop && accepted)
+ {
+ getRoot()->autoOpenTest(this);
+ }
+
+ return TRUE;
+}
+
+
+BOOL LLFolderViewFolder::handleRightMouseDown( S32 x, S32 y, MASK mask )
+{
+ BOOL handled = FALSE;
+
+ if( isOpen() )
+ {
+ handled = childrenHandleRightMouseDown( x, y, mask ) != NULL;
+ }
+ if (!handled)
+ {
+ handled = LLFolderViewItem::handleRightMouseDown( x, y, mask );
+ }
+ return handled;
+}
+
+
+BOOL LLFolderViewFolder::handleHover(S32 x, S32 y, MASK mask)
+{
+ mIsMouseOverTitle = (y > (getRect().getHeight() - mItemHeight));
+
+ BOOL handled = LLView::handleHover(x, y, mask);
+
+ if (!handled)
+ {
+ // this doesn't do child processing
+ handled = LLFolderViewItem::handleHover(x, y, mask);
+ }
+
+ return handled;
+}
+
+BOOL LLFolderViewFolder::handleMouseDown( S32 x, S32 y, MASK mask )
+{
+ BOOL handled = FALSE;
+ if( isOpen() )
+ {
+ handled = childrenHandleMouseDown(x,y,mask) != NULL;
+ }
+ if( !handled )
+ {
+ if(mIndentation < x && x < mIndentation + (isCollapsed() ? 0 : mArrowSize) + mTextPad)
+ {
+ toggleOpen();
+ handled = TRUE;
+ }
+ else
+ {
+ // do normal selection logic
+ handled = LLFolderViewItem::handleMouseDown(x, y, mask);
+ }
+ }
+
+ return handled;
+}
+
+BOOL LLFolderViewFolder::handleDoubleClick( S32 x, S32 y, MASK mask )
+{
+ BOOL handled = FALSE;
+ if( isOpen() )
+ {
+ handled = childrenHandleDoubleClick( x, y, mask ) != NULL;
+ }
+ if( !handled )
+ {
+ if(mIndentation < x && x < mIndentation + (isCollapsed() ? 0 : mArrowSize) + mTextPad)
+ {
+ // don't select when user double-clicks plus sign
+ // so as not to contradict single-click behavior
+ toggleOpen();
+ }
+ else
+ {
+ getRoot()->setSelection(this, FALSE);
+ toggleOpen();
+ }
+ handled = TRUE;
+ }
+ return handled;
+}
+
+void LLFolderViewFolder::draw()
+{
+ updateLabelRotation();
+
+ LLFolderViewItem::draw();
+
+ // draw children if root folder, or any other folder that is open or animating to closed state
+ if( getRoot() == this || (isOpen() || mCurHeight != mTargetHeight ))
+ {
+ LLView::draw();
+ }
+
+ mExpanderHighlighted = FALSE;
+}
+
+// this does prefix traversal, as folders are listed above their contents
+LLFolderViewItem* LLFolderViewFolder::getNextFromChild( LLFolderViewItem* item, BOOL include_children )
+{
+ BOOL found_item = FALSE;
+
+ LLFolderViewItem* result = NULL;
+ // when not starting from a given item, start at beginning
+ if(item == NULL)
+ {
+ found_item = TRUE;
+ }
+
+ // find current item among children
+ folders_t::iterator fit = mFolders.begin();
+ folders_t::iterator fend = mFolders.end();
+
+ items_t::iterator iit = mItems.begin();
+ items_t::iterator iend = mItems.end();
+
+ // if not trivially starting at the beginning, we have to find the current item
+ if (!found_item)
+ {
+ // first, look among folders, since they are always above items
+ for(; fit != fend; ++fit)
+ {
+ if(item == (*fit))
+ {
+ found_item = TRUE;
+ // if we are on downwards traversal
+ if (include_children && (*fit)->isOpen())
+ {
+ // look for first descendant
+ return (*fit)->getNextFromChild(NULL, TRUE);
+ }
+ // otherwise advance to next folder
+ ++fit;
+ include_children = TRUE;
+ break;
+ }
+ }
+
+ // didn't find in folders? Check items...
+ if (!found_item)
+ {
+ for(; iit != iend; ++iit)
+ {
+ if(item == (*iit))
+ {
+ found_item = TRUE;
+ // point to next item
+ ++iit;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!found_item)
+ {
+ // you should never call this method with an item that isn't a child
+ // so we should always find something
+ llassert(FALSE);
+ return NULL;
+ }
+
+ // at this point, either iit or fit point to a candidate "next" item
+ // if both are out of range, we need to punt up to our parent
+
+ // now, starting from found folder, continue through folders
+ // searching for next visible folder
+ while(fit != fend && !(*fit)->getVisible())
+ {
+ // turn on downwards traversal for next folder
+ ++fit;
+ }
+
+ if (fit != fend)
+ {
+ result = (*fit);
+ }
+ else
+ {
+ // otherwise, scan for next visible item
+ while(iit != iend && !(*iit)->getVisible())
+ {
+ ++iit;
+ }
+
+ // check to see if we have a valid item
+ if (iit != iend)
+ {
+ result = (*iit);
+ }
+ }
+
+ if( !result && mParentFolder )
+ {
+ // If there are no siblings or children to go to, recurse up one level in the tree
+ // and skip children for this folder, as we've already discounted them
+ result = mParentFolder->getNextFromChild(this, FALSE);
+ }
+
+ return result;
+}
+
+// this does postfix traversal, as folders are listed above their contents
+LLFolderViewItem* LLFolderViewFolder::getPreviousFromChild( LLFolderViewItem* item, BOOL include_children )
+{
+ BOOL found_item = FALSE;
+
+ LLFolderViewItem* result = NULL;
+ // when not starting from a given item, start at end
+ if(item == NULL)
+ {
+ found_item = TRUE;
+ }
+
+ // find current item among children
+ folders_t::reverse_iterator fit = mFolders.rbegin();
+ folders_t::reverse_iterator fend = mFolders.rend();
+
+ items_t::reverse_iterator iit = mItems.rbegin();
+ items_t::reverse_iterator iend = mItems.rend();
+
+ // if not trivially starting at the end, we have to find the current item
+ if (!found_item)
+ {
+ // first, look among items, since they are always below the folders
+ for(; iit != iend; ++iit)
+ {
+ if(item == (*iit))
+ {
+ found_item = TRUE;
+ // point to next item
+ ++iit;
+ break;
+ }
+ }
+
+ // didn't find in items? Check folders...
+ if (!found_item)
+ {
+ for(; fit != fend; ++fit)
+ {
+ if(item == (*fit))
+ {
+ found_item = TRUE;
+ // point to next folder
+ ++fit;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!found_item)
+ {
+ // you should never call this method with an item that isn't a child
+ // so we should always find something
+ llassert(FALSE);
+ return NULL;
+ }
+
+ // at this point, either iit or fit point to a candidate "next" item
+ // if both are out of range, we need to punt up to our parent
+
+ // now, starting from found item, continue through items
+ // searching for next visible item
+ while(iit != iend && !(*iit)->getVisible())
+ {
+ ++iit;
+ }
+
+ if (iit != iend)
+ {
+ // we found an appropriate item
+ result = (*iit);
+ }
+ else
+ {
+ // otherwise, scan for next visible folder
+ while(fit != fend && !(*fit)->getVisible())
+ {
+ ++fit;
+ }
+
+ // check to see if we have a valid folder
+ if (fit != fend)
+ {
+ // try selecting child element of this folder
+ if ((*fit)->isOpen() && include_children)
+ {
+ result = (*fit)->getPreviousFromChild(NULL);
+ }
+ else
+ {
+ result = (*fit);
+ }
+ }
+ }
+
+ if( !result )
+ {
+ // If there are no siblings or children to go to, recurse up one level in the tree
+ // which gets back to this folder, which will only be visited if it is a valid, visible item
+ result = this;
+ }
+
+ return result;
+}
+
diff --git a/indra/llui/llfolderviewitem.h b/indra/llui/llfolderviewitem.h
new file mode 100755
index 0000000000..5ad5731cad
--- /dev/null
+++ b/indra/llui/llfolderviewitem.h
@@ -0,0 +1,465 @@
+/**
+* @file llfolderviewitem.h
+* @brief Items and folders that can appear in a hierarchical folder view
+*
+* $LicenseInfo:firstyear=2001&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2010, Linden Research, Inc.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation;
+* version 2.1 of the License only.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+* $/LicenseInfo$
+*/
+#ifndef LLFOLDERVIEWITEM_H
+#define LLFOLDERVIEWITEM_H
+
+#include "llflashtimer.h"
+#include "llview.h"
+#include "lluiimage.h"
+
+class LLFolderView;
+class LLFolderViewModelItem;
+class LLFolderViewFolder;
+class LLFolderViewFunctor;
+class LLFolderViewFilter;
+class LLFolderViewModelInterface;
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLFolderViewItem
+//
+// An instance of this class represents a single item in a folder view
+// such as an inventory item or a file.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLFolderViewItem : public LLView
+{
+public:
+ struct Params : public LLInitParam::Block<Params, LLView::Params>
+ {
+ Optional<LLUIImage*> folder_arrow_image,
+ selection_image;
+ Mandatory<LLFolderView*> root;
+ Mandatory<LLFolderViewModelItem*> listener;
+
+ Optional<S32> folder_indentation, // pixels
+ item_height,
+ item_top_pad;
+
+ Optional<time_t> creation_date;
+ Optional<bool> allow_open;
+
+ Optional<LLUIColor> font_color;
+ Optional<LLUIColor> font_highlight_color;
+
+ Optional<S32> left_pad,
+ icon_pad,
+ icon_width,
+ text_pad,
+ text_pad_right,
+ arrow_size,
+ max_folder_item_overlap;
+ Params();
+ };
+
+
+ static const S32 DEFAULT_LABEL_PADDING_RIGHT = 4;
+ // animation parameters
+ static const F32 FOLDER_CLOSE_TIME_CONSTANT,
+ FOLDER_OPEN_TIME_CONSTANT;
+
+protected:
+ friend class LLUICtrlFactory;
+ friend class LLFolderViewModelItem;
+
+ LLFolderViewItem(const Params& p);
+
+ std::string mLabel;
+ S32 mLabelWidth;
+ bool mLabelWidthDirty;
+ S32 mLabelPaddingRight;
+ LLFolderViewFolder* mParentFolder;
+ LLPointer<LLFolderViewModelItem> mViewModelItem;
+ LLFontGL::StyleFlags mLabelStyle;
+ std::string mLabelSuffix;
+ LLUIImagePtr mIcon,
+ mIconOpen,
+ mIconOverlay;
+ S32 mLocalIndentation;
+ S32 mIndentation;
+ S32 mItemHeight;
+ S32 mDragStartX,
+ mDragStartY;
+
+ S32 mLeftPad,
+ mIconPad,
+ mIconWidth,
+ mTextPad,
+ mTextPadRight,
+ mArrowSize,
+ mMaxFolderItemOverlap;
+
+ F32 mControlLabelRotation;
+ LLFolderView* mRoot;
+ bool mHasVisibleChildren,
+ mIsCurSelection,
+ mDragAndDropTarget,
+ mIsMouseOverTitle,
+ mAllowOpen,
+ mSelectPending;
+
+ LLUIColor mFontColor;
+ LLUIColor mFontHighlightColor;
+
+ // For now assuming all colors are the same in derived classes.
+ static bool sColorSetInitialized;
+ static LLUIColor sFgColor;
+ static LLUIColor sFgDisabledColor;
+ static LLUIColor sHighlightBgColor;
+ static LLUIColor sFlashBgColor;
+ static LLUIColor sFocusOutlineColor;
+ static LLUIColor sMouseOverColor;
+ static LLUIColor sFilterBGColor;
+ static LLUIColor sFilterTextColor;
+ static LLUIColor sSuffixColor;
+ static LLUIColor sSearchStatusColor;
+
+ // this is an internal method used for adding items to folders. A
+ // no-op at this level, but reimplemented in derived classes.
+ virtual void addItem(LLFolderViewItem*) { }
+ virtual void addFolder(LLFolderViewFolder*) { }
+ virtual bool isHighlightAllowed();
+ virtual bool isHighlightActive();
+ virtual bool isFlashing() { return false; }
+ virtual void setFlashState(bool) { }
+
+ static LLFontGL* getLabelFontForStyle(U8 style);
+
+ BOOL mIsSelected;
+
+public:
+ static void initClass();
+ static void cleanupClass();
+
+ BOOL postBuild();
+
+ virtual void openItem( void );
+
+ void arrangeAndSet(BOOL set_selection, BOOL take_keyboard_focus);
+
+ virtual ~LLFolderViewItem( void );
+
+ // addToFolder() returns TRUE if it succeeds. FALSE otherwise
+ virtual void addToFolder(LLFolderViewFolder* folder);
+
+ // Finds width and height of this object and it's children. Also
+ // makes sure that this view and it's children are the right size.
+ virtual S32 arrange( S32* width, S32* height );
+ virtual S32 getItemHeight();
+ virtual S32 getLabelXPos();
+ S32 getIconPad();
+ S32 getTextPad();
+
+ // If 'selection' is 'this' then note that otherwise ignore.
+ // Returns TRUE if this item ends up being selected.
+ virtual BOOL setSelection(LLFolderViewItem* selection, BOOL openitem, BOOL take_keyboard_focus);
+
+ // This method is used to set the selection state of an item.
+ // If 'selection' is 'this' then note selection.
+ // Returns TRUE if the selection state of this item was changed.
+ virtual BOOL changeSelection(LLFolderViewItem* selection, BOOL selected);
+
+ // this method is used to deselect this element
+ void deselectItem();
+
+ // this method is used to select this element
+ virtual void selectItem();
+
+ // gets multiple-element selection
+ virtual std::set<LLFolderViewItem*> getSelectionList() const;
+
+ // Returns true is this object and all of its children can be removed (deleted by user)
+ virtual BOOL isRemovable();
+
+ // Returns true is this object and all of its children can be moved
+ virtual BOOL isMovable();
+
+ // destroys this item recursively
+ virtual void destroyView();
+
+ BOOL isSelected() const { return mIsSelected; }
+ bool isInSelection() const;
+
+ void setUnselected() { mIsSelected = FALSE; }
+
+ void setIsCurSelection(BOOL select) { mIsCurSelection = select; }
+
+ BOOL getIsCurSelection() { return mIsCurSelection; }
+
+ BOOL hasVisibleChildren() { return mHasVisibleChildren; }
+
+ // Call through to the viewed object and return true if it can be
+ // removed. Returns true if it's removed.
+ //virtual BOOL removeRecursively(BOOL single_item);
+ BOOL remove();
+
+ // Build an appropriate context menu for the item. Flags unused.
+ void buildContextMenu(class LLMenuGL& menu, U32 flags);
+
+ // This method returns the actual name of the thing being
+ // viewed. This method will ask the viewed object itself.
+ const std::string& getName( void ) const;
+
+ // This method returns the label displayed on the view. This
+ // method was primarily added to allow sorting on the folder
+ // contents possible before the entire view has been constructed.
+ const std::string& getLabel() const { return mLabel; }
+
+ LLFolderViewFolder* getParentFolder( void ) { return mParentFolder; }
+ const LLFolderViewFolder* getParentFolder( void ) const { return mParentFolder; }
+
+ void setParentFolder(LLFolderViewFolder* parent) { mParentFolder = parent; }
+
+ LLFolderViewItem* getNextOpenNode( BOOL include_children = TRUE );
+ LLFolderViewItem* getPreviousOpenNode( BOOL include_children = TRUE );
+
+ const LLFolderViewModelItem* getViewModelItem( void ) const { return mViewModelItem; }
+ LLFolderViewModelItem* getViewModelItem( void ) { return mViewModelItem; }
+
+ const LLFolderViewModelInterface* getFolderViewModel( void ) const;
+ LLFolderViewModelInterface* getFolderViewModel( void );
+
+ // just rename the object.
+ void rename(const std::string& new_name);
+
+ // Show children
+ virtual void setOpen(BOOL open = TRUE) {};
+ virtual BOOL isOpen() const { return FALSE; }
+
+ virtual LLFolderView* getRoot();
+ virtual const LLFolderView* getRoot() const;
+ BOOL isDescendantOf( const LLFolderViewFolder* potential_ancestor );
+ S32 getIndentation() { return mIndentation; }
+
+ virtual BOOL passedFilter(S32 filter_generation = -1);
+ virtual BOOL isPotentiallyVisible(S32 filter_generation = -1);
+
+ // refresh information from the object being viewed.
+ virtual void refresh();
+
+ // LLView functionality
+ virtual BOOL handleRightMouseDown( S32 x, S32 y, MASK mask );
+ virtual BOOL handleMouseDown( S32 x, S32 y, MASK mask );
+ virtual BOOL handleHover( S32 x, S32 y, MASK mask );
+ virtual BOOL handleMouseUp( S32 x, S32 y, MASK mask );
+ virtual BOOL handleDoubleClick( S32 x, S32 y, MASK mask );
+
+ virtual void onMouseLeave(S32 x, S32 y, MASK mask);
+
+ //virtual LLView* findChildView(const std::string& name, BOOL recurse) const { return LLView::findChildView(name, recurse); }
+
+ // virtual void handleDropped();
+ virtual void draw();
+ void drawOpenFolderArrow(const Params& default_params, const LLUIColor& fg_color);
+ void drawHighlight(const BOOL showContent, const BOOL hasKeyboardFocus, const LLUIColor &selectColor, const LLUIColor &flashColor, const LLUIColor &outlineColor, const LLUIColor &mouseOverColor);
+ void drawLabel(const LLFontGL * font, const F32 x, const F32 y, const LLColor4& color, F32 &right_x);
+ virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ std::string& tooltip_msg);
+
+private:
+ static std::map<U8, LLFontGL*> sFonts; // map of styles to fonts
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLFolderViewFolder
+//
+// An instance of an LLFolderViewFolder represents a collection of
+// more folders and items. This is used to build the hierarchy of
+// items found in the folder view.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLFolderViewFolder : public LLFolderViewItem
+{
+protected:
+ LLFolderViewFolder( const LLFolderViewItem::Params& );
+ friend class LLUICtrlFactory;
+
+ void updateLabelRotation();
+ virtual bool isCollapsed() { return FALSE; }
+
+public:
+ typedef std::list<LLFolderViewItem*> items_t;
+ typedef std::list<LLFolderViewFolder*> folders_t;
+
+protected:
+ items_t mItems;
+ folders_t mFolders;
+
+ BOOL mIsOpen;
+ BOOL mExpanderHighlighted;
+ F32 mCurHeight;
+ F32 mTargetHeight;
+ F32 mAutoOpenCountdown;
+ S32 mLastArrangeGeneration;
+ S32 mLastCalculatedWidth;
+ bool mNeedsSort;
+
+public:
+ typedef enum e_recurse_type
+ {
+ RECURSE_NO,
+ RECURSE_UP,
+ RECURSE_DOWN,
+ RECURSE_UP_DOWN
+ } ERecurseType;
+
+
+ virtual ~LLFolderViewFolder( void );
+
+ LLFolderViewItem* getNextFromChild( LLFolderViewItem*, BOOL include_children = TRUE );
+ LLFolderViewItem* getPreviousFromChild( LLFolderViewItem*, BOOL include_children = TRUE );
+
+ // addToFolder() returns TRUE if it succeeds. FALSE otherwise
+ virtual void addToFolder(LLFolderViewFolder* folder);
+
+ // Finds width and height of this object and it's children. Also
+ // makes sure that this view and it's children are the right size.
+ virtual S32 arrange( S32* width, S32* height );
+
+ BOOL needsArrange();
+
+ bool descendantsPassedFilter(S32 filter_generation = -1);
+
+ // Passes selection information on to children and record
+ // selection information if necessary.
+ // Returns TRUE if this object (or a child) ends up being selected.
+ // If 'openitem' is TRUE then folders are opened up along the way to the selection.
+ virtual BOOL setSelection(LLFolderViewItem* selection, BOOL openitem, BOOL take_keyboard_focus = TRUE);
+
+ // This method is used to change the selection of an item.
+ // Recursively traverse all children; if 'selection' is 'this' then change
+ // the select status if necessary.
+ // Returns TRUE if the selection state of this folder, or of a child, was changed.
+ virtual BOOL changeSelection(LLFolderViewItem* selection, BOOL selected);
+
+ // this method is used to group select items
+ void extendSelectionTo(LLFolderViewItem* selection);
+
+ // Returns true is this object and all of its children can be removed.
+ virtual BOOL isRemovable();
+
+ // Returns true is this object and all of its children can be moved
+ virtual BOOL isMovable();
+
+ // destroys this folder, and all children
+ virtual void destroyView();
+
+ // extractItem() removes the specified item from the folder, but
+ // doesn't delete it.
+ virtual void extractItem( LLFolderViewItem* item );
+
+ // This function is called by a child that needs to be resorted.
+ void resort(LLFolderViewItem* item);
+
+ void setAutoOpenCountdown(F32 countdown) { mAutoOpenCountdown = countdown; }
+
+ // folders can be opened. This will usually be called by internal
+ // methods.
+ virtual void toggleOpen();
+
+ // Force a folder open or closed
+ virtual void setOpen(BOOL openitem = TRUE);
+
+ // Called when a child is refreshed.
+ virtual void requestArrange();
+
+ // internal method which doesn't update the entire view. This
+ // method was written because the list iterators destroy the state
+ // of other iterations, thus, we can't arrange while iterating
+ // through the children (such as when setting which is selected.
+ virtual void setOpenArrangeRecursively(BOOL openitem, ERecurseType recurse = RECURSE_NO);
+
+ // Get the current state of the folder.
+ virtual BOOL isOpen() const { return mIsOpen; }
+
+ // special case if an object is dropped on the child.
+ BOOL handleDragAndDropFromChild(MASK mask,
+ BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ std::string& tooltip_msg);
+
+
+ // Just apply this functor to the folder's immediate children.
+ void applyFunctorToChildren(LLFolderViewFunctor& functor);
+ // apply this functor to the folder's descendants.
+ void applyFunctorRecursively(LLFolderViewFunctor& functor);
+
+ virtual void openItem( void );
+
+ // LLView functionality
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+ virtual BOOL handleRightMouseDown( S32 x, S32 y, MASK mask );
+ virtual BOOL handleMouseDown( S32 x, S32 y, MASK mask );
+ virtual BOOL handleDoubleClick( S32 x, S32 y, MASK mask );
+ virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask,
+ BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ std::string& tooltip_msg);
+ BOOL handleDragAndDropToThisFolder(MASK mask,
+ BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ std::string& tooltip_msg);
+ virtual void draw();
+
+ folders_t::iterator getFoldersBegin() { return mFolders.begin(); }
+ folders_t::iterator getFoldersEnd() { return mFolders.end(); }
+ folders_t::size_type getFoldersCount() const { return mFolders.size(); }
+
+ items_t::const_iterator getItemsBegin() const { return mItems.begin(); }
+ items_t::const_iterator getItemsEnd() const { return mItems.end(); }
+ items_t::size_type getItemsCount() const { return mItems.size(); }
+
+ LLFolderViewFolder* getCommonAncestor(LLFolderViewItem* item_a, LLFolderViewItem* item_b, bool& reverse);
+ void gatherChildRangeExclusive(LLFolderViewItem* start, LLFolderViewItem* end, bool reverse, std::vector<LLFolderViewItem*>& items);
+
+ // internal functions for tracking folders and items separately
+ // use addToFolder() virtual method to ensure folders are always added to mFolders
+ // and not mItems
+ void addItem(LLFolderViewItem* item);
+ void addFolder( LLFolderViewFolder* folder);
+
+ //WARNING: do not call directly...use the appropriate LLFolderViewModel-derived class instead
+ template<typename SORT_FUNC> void sortFolders(const SORT_FUNC& func) { mFolders.sort(func); }
+ template<typename SORT_FUNC> void sortItems(const SORT_FUNC& func) { mItems.sort(func); }
+};
+
+typedef std::deque<LLFolderViewItem*> folder_view_item_deque;
+
+class LLFolderViewGroupedItemModel: public LLRefCount
+{
+public:
+ virtual void groupFilterContextMenu(folder_view_item_deque& selected_items, LLMenuGL& menu) = 0;
+};
+
+#endif // LLFOLDERVIEWITEM_H
diff --git a/indra/llui/llfolderviewmodel.cpp b/indra/llui/llfolderviewmodel.cpp
new file mode 100755
index 0000000000..3363dc5316
--- /dev/null
+++ b/indra/llui/llfolderviewmodel.cpp
@@ -0,0 +1,68 @@
+/**
+ * @file llfolderviewmodel.cpp
+ * @brief Implementation of the view model collection of classes.
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llfolderviewmodel.h"
+#include "lltrans.h"
+
+bool LLFolderViewModelCommon::needsSort(LLFolderViewModelItem* item)
+{
+ return item->getSortVersion() < mTargetSortVersion;
+}
+
+std::string LLFolderViewModelCommon::getStatusText()
+{
+ if (!contentsReady() || mFolderView->getViewModelItem()->getLastFilterGeneration() < getFilter().getCurrentGeneration())
+ {
+ return LLTrans::getString("Searching");
+ }
+ else
+ {
+ return getFilter().getEmptyLookupMessage();
+ }
+}
+
+void LLFolderViewModelCommon::filter()
+{
+ getFilter().resetTime(llclamp(LLUI::sSettingGroups["config"]->getS32("FilterItemsMaxTimePerFrameVisible"), 1, 100));
+ mFolderView->getViewModelItem()->filter(getFilter());
+}
+
+bool LLFolderViewModelItemCommon::hasFilterStringMatch()
+{
+ return mStringMatchOffsetFilter != std::string::npos;
+}
+
+std::string::size_type LLFolderViewModelItemCommon::getFilterStringOffset()
+{
+ return mStringMatchOffsetFilter;
+}
+
+std::string::size_type LLFolderViewModelItemCommon::getFilterStringSize()
+{
+ return mRootViewModel.getFilter().getFilterStringSize();
+}
diff --git a/indra/llui/llfolderviewmodel.h b/indra/llui/llfolderviewmodel.h
new file mode 100755
index 0000000000..f6550eae42
--- /dev/null
+++ b/indra/llui/llfolderviewmodel.h
@@ -0,0 +1,481 @@
+/**
+ * @file llfolderviewmodel.h
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+#ifndef LLFOLDERVIEWMODEL_H
+#define LLFOLDERVIEWMODEL_H
+
+#include "llfontgl.h" // just for StyleFlags enum
+#include "llfolderview.h"
+
+// These are grouping of inventory types.
+// Order matters when sorting system folders to the top.
+enum EInventorySortGroup
+{
+ SG_SYSTEM_FOLDER,
+ SG_TRASH_FOLDER,
+ SG_NORMAL_FOLDER,
+ SG_ITEM
+};
+
+class LLFontGL;
+class LLInventoryModel;
+class LLMenuGL;
+class LLUIImage;
+class LLUUID;
+class LLFolderViewItem;
+class LLFolderViewFolder;
+
+class LLFolderViewFilter
+{
+public:
+ enum EFilterModified
+ {
+ FILTER_NONE, // nothing to do, already filtered
+ FILTER_RESTART, // restart filtering from scratch
+ FILTER_LESS_RESTRICTIVE, // existing filtered items will certainly pass this filter
+ FILTER_MORE_RESTRICTIVE // if you didn't pass the previous filter, you definitely won't pass this one
+ };
+
+public:
+
+ LLFolderViewFilter() {}
+ virtual ~LLFolderViewFilter() {}
+
+ // +-------------------------------------------------------------------+
+ // + Execution And Results
+ // +-------------------------------------------------------------------+
+ virtual bool check(const LLFolderViewModelItem* item) = 0;
+ virtual bool checkFolder(const LLFolderViewModelItem* folder) const = 0;
+
+ virtual void setEmptyLookupMessage(const std::string& message) = 0;
+ virtual std::string getEmptyLookupMessage() const = 0;
+
+ virtual bool showAllResults() const = 0;
+
+ virtual std::string::size_type getStringMatchOffset(LLFolderViewModelItem* item) const = 0;
+ virtual std::string::size_type getFilterStringSize() const = 0;
+ // +-------------------------------------------------------------------+
+ // + Status
+ // +-------------------------------------------------------------------+
+ virtual bool isActive() const = 0;
+ virtual bool isModified() const = 0;
+ virtual void clearModified() = 0;
+ virtual const std::string& getName() const = 0;
+ virtual const std::string& getFilterText() = 0;
+ //RN: this is public to allow system to externally force a global refilter
+ virtual void setModified(EFilterModified behavior = FILTER_RESTART) = 0;
+
+ // +-------------------------------------------------------------------+
+ // + Time
+ // +-------------------------------------------------------------------+
+ virtual void resetTime(S32 timeout) = 0;
+ virtual bool isTimedOut() = 0;
+
+ // +-------------------------------------------------------------------+
+ // + Default
+ // +-------------------------------------------------------------------+
+ virtual bool isDefault() const = 0;
+ virtual bool isNotDefault() const = 0;
+ virtual void markDefault() = 0;
+ virtual void resetDefault() = 0;
+
+ // +-------------------------------------------------------------------+
+ // + Generation
+ // +-------------------------------------------------------------------+
+ virtual S32 getCurrentGeneration() const = 0;
+ virtual S32 getFirstSuccessGeneration() const = 0;
+ virtual S32 getFirstRequiredGeneration() const = 0;
+};
+
+class LLFolderViewModelInterface : public LLTrace::MemTrackable<LLFolderViewModelInterface>
+{
+public:
+ LLFolderViewModelInterface()
+ : LLTrace::MemTrackable<LLFolderViewModelInterface>("LLFolderViewModelInterface")
+ {}
+
+ virtual ~LLFolderViewModelInterface() {}
+ virtual void requestSortAll() = 0;
+
+ virtual void sort(class LLFolderViewFolder*) = 0;
+ virtual void filter() = 0;
+
+ virtual bool contentsReady() = 0;
+ virtual void setFolderView(LLFolderView* folder_view) = 0;
+ virtual LLFolderViewFilter& getFilter() = 0;
+ virtual const LLFolderViewFilter& getFilter() const = 0;
+ virtual std::string getStatusText() = 0;
+
+ virtual bool startDrag(std::vector<LLFolderViewModelItem*>& items) = 0;
+};
+
+// This is an abstract base class that users of the folderview classes
+// would use to bridge the folder view with the underlying data
+class LLFolderViewModelItem : public LLRefCount, public LLTrace::MemTrackable<LLFolderViewModelItem>
+{
+public:
+ LLFolderViewModelItem()
+ : LLTrace::MemTrackable<LLFolderViewModelItem>("LLFolderViewModelItem")
+ {}
+
+ virtual ~LLFolderViewModelItem() { }
+
+ virtual void update() {} //called when drawing
+ virtual const std::string& getName() const = 0;
+ virtual const std::string& getDisplayName() const = 0;
+ virtual const std::string& getSearchableName() const = 0;
+
+ virtual LLPointer<LLUIImage> getIcon() const = 0;
+ virtual LLPointer<LLUIImage> getIconOpen() const { return getIcon(); }
+ virtual LLPointer<LLUIImage> getIconOverlay() const { return NULL; }
+
+ virtual LLFontGL::StyleFlags getLabelStyle() const = 0;
+ virtual std::string getLabelSuffix() const = 0;
+
+ virtual void openItem( void ) = 0;
+ virtual void closeItem( void ) = 0;
+ virtual void selectItem(void) = 0;
+
+ virtual BOOL isItemRenameable() const = 0;
+ virtual BOOL renameItem(const std::string& new_name) = 0;
+
+ virtual BOOL isItemMovable( void ) const = 0; // Can be moved to another folder
+ virtual void move( LLFolderViewModelItem* parent_listener ) = 0;
+
+ virtual BOOL isItemRemovable( void ) const = 0; // Can be destroyed
+ virtual BOOL removeItem() = 0;
+ virtual void removeBatch(std::vector<LLFolderViewModelItem*>& batch) = 0;
+
+ virtual BOOL isItemCopyable() const = 0;
+ virtual BOOL copyToClipboard() const = 0;
+ virtual BOOL cutToClipboard() const = 0;
+
+ virtual BOOL isClipboardPasteable() const = 0;
+ virtual void pasteFromClipboard() = 0;
+ virtual void pasteLinkFromClipboard() = 0;
+
+ virtual void buildContextMenu(LLMenuGL& menu, U32 flags) = 0;
+
+ virtual bool potentiallyVisible() = 0; // is the item definitely visible or we haven't made up our minds yet?
+
+ virtual bool filter( LLFolderViewFilter& filter) = 0;
+ virtual bool passedFilter(S32 filter_generation = -1) = 0;
+ virtual bool descendantsPassedFilter(S32 filter_generation = -1) = 0;
+ virtual void setPassedFilter(bool passed, S32 filter_generation, std::string::size_type string_offset = std::string::npos, std::string::size_type string_size = 0) = 0;
+ virtual void setPassedFolderFilter(bool passed, S32 filter_generation) = 0;
+ virtual void dirtyFilter() = 0;
+ virtual void dirtyDescendantsFilter() = 0;
+ virtual bool hasFilterStringMatch() = 0;
+ virtual std::string::size_type getFilterStringOffset() = 0;
+ virtual std::string::size_type getFilterStringSize() = 0;
+
+ virtual S32 getLastFilterGeneration() const = 0;
+ virtual S32 getMarkedDirtyGeneration() const = 0;
+
+ virtual bool hasChildren() const = 0;
+ virtual void addChild(LLFolderViewModelItem* child) = 0;
+ virtual void removeChild(LLFolderViewModelItem* child) = 0;
+
+ // This method will be called to determine if a drop can be
+ // performed, and will set drop to TRUE if a drop is
+ // requested. Returns TRUE if a drop is possible/happened,
+ // otherwise FALSE.
+ virtual BOOL dragOrDrop(MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ std::string& tooltip_msg) = 0;
+
+ virtual void requestSort() = 0;
+ virtual S32 getSortVersion() = 0;
+ virtual void setSortVersion(S32 version) = 0;
+ virtual void setParent(LLFolderViewModelItem* parent) = 0;
+ virtual bool hasParent() = 0;
+
+protected:
+
+ friend class LLFolderViewItem;
+ virtual void setFolderViewItem(LLFolderViewItem* folder_view_item) = 0;
+
+};
+
+
+class LLFolderViewModelItemCommon : public LLFolderViewModelItem
+{
+public:
+ LLFolderViewModelItemCommon(LLFolderViewModelInterface& root_view_model)
+ : mSortVersion(-1),
+ mPassedFilter(true),
+ mPassedFolderFilter(true),
+ mStringMatchOffsetFilter(std::string::npos),
+ mStringFilterSize(0),
+ mFolderViewItem(NULL),
+ mLastFilterGeneration(-1),
+ mLastFolderFilterGeneration(-1),
+ mMarkedDirtyGeneration(-1),
+ mMostFilteredDescendantGeneration(-1),
+ mParent(NULL),
+ mRootViewModel(root_view_model)
+ {
+ mChildren.clear();
+ }
+
+ void requestSort() { mSortVersion = -1; }
+ S32 getSortVersion() { return mSortVersion; }
+ void setSortVersion(S32 version) { mSortVersion = version;}
+
+ S32 getLastFilterGeneration() const { return mLastFilterGeneration; }
+ S32 getLastFolderFilterGeneration() const { return mLastFolderFilterGeneration; }
+ S32 getMarkedDirtyGeneration() const { return mMarkedDirtyGeneration; }
+ void dirtyFilter()
+ {
+ if(mMarkedDirtyGeneration < 0)
+ {
+ mMarkedDirtyGeneration = mLastFilterGeneration;
+ }
+ mLastFilterGeneration = -1;
+ mLastFolderFilterGeneration = -1;
+
+ // bubble up dirty flag all the way to root
+ if (mParent)
+ {
+ mParent->dirtyFilter();
+ }
+ }
+ void dirtyDescendantsFilter()
+ {
+ mMostFilteredDescendantGeneration = -1;
+ if (mParent)
+ {
+ mParent->dirtyDescendantsFilter();
+ }
+ }
+ bool hasFilterStringMatch();
+ std::string::size_type getFilterStringOffset();
+ std::string::size_type getFilterStringSize();
+
+ typedef std::list<LLFolderViewModelItem*> child_list_t;
+
+ virtual void addChild(LLFolderViewModelItem* child)
+ {
+ // Avoid duplicates: bail out if that child is already present in the list
+ // Note: this happens when models are created before views
+ child_list_t::const_iterator iter;
+ for (iter = mChildren.begin(); iter != mChildren.end(); iter++)
+ {
+ if (child == *iter)
+ {
+ return;
+ }
+ }
+ mChildren.push_back(child);
+ child->setParent(this);
+ dirtyFilter();
+ requestSort();
+ }
+ virtual void removeChild(LLFolderViewModelItem* child)
+ {
+ mChildren.remove(child);
+ child->setParent(NULL);
+ dirtyDescendantsFilter();
+ dirtyFilter();
+ }
+
+ virtual void clearChildren()
+ {
+ // As this is cleaning the whole list of children wholesale, we do need to delete the pointed objects
+ // This is different and not equivalent to calling removeChild() on each child
+ std::for_each(mChildren.begin(), mChildren.end(), DeletePointer());
+ mChildren.clear();
+ dirtyDescendantsFilter();
+ dirtyFilter();
+ }
+
+ child_list_t::const_iterator getChildrenBegin() const { return mChildren.begin(); }
+ child_list_t::const_iterator getChildrenEnd() const { return mChildren.end(); }
+ child_list_t::size_type getChildrenCount() const { return mChildren.size(); }
+
+ void setPassedFilter(bool passed, S32 filter_generation, std::string::size_type string_offset = std::string::npos, std::string::size_type string_size = 0)
+ {
+ mPassedFilter = passed;
+ mLastFilterGeneration = filter_generation;
+ mStringMatchOffsetFilter = string_offset;
+ mStringFilterSize = string_size;
+ mMarkedDirtyGeneration = -1;
+ }
+
+ void setPassedFolderFilter(bool passed, S32 filter_generation)
+ {
+ mPassedFolderFilter = passed;
+ mLastFolderFilterGeneration = filter_generation;
+ }
+
+ virtual bool potentiallyVisible()
+ {
+ return passedFilter() // we've passed the filter
+ || (getLastFilterGeneration() < mRootViewModel.getFilter().getFirstSuccessGeneration()) // or we don't know yet
+ || descendantsPassedFilter();
+ }
+
+ virtual bool passedFilter(S32 filter_generation = -1)
+ {
+ if (filter_generation < 0)
+ {
+ filter_generation = mRootViewModel.getFilter().getFirstSuccessGeneration();
+ }
+ bool passed_folder_filter = mPassedFolderFilter && (mLastFolderFilterGeneration >= filter_generation);
+ bool passed_filter = mPassedFilter && (mLastFilterGeneration >= filter_generation);
+ return passed_folder_filter && (passed_filter || descendantsPassedFilter(filter_generation));
+ }
+
+ virtual bool descendantsPassedFilter(S32 filter_generation = -1)
+ {
+ if (filter_generation < 0)
+ {
+ filter_generation = mRootViewModel.getFilter().getFirstSuccessGeneration();
+ }
+ return mMostFilteredDescendantGeneration >= filter_generation;
+ }
+
+
+protected:
+ virtual void setParent(LLFolderViewModelItem* parent) { mParent = parent; }
+ virtual bool hasParent() { return mParent != NULL; }
+
+ S32 mSortVersion;
+ bool mPassedFilter;
+ bool mPassedFolderFilter;
+ std::string::size_type mStringMatchOffsetFilter;
+ std::string::size_type mStringFilterSize;
+
+ S32 mLastFilterGeneration,
+ mLastFolderFilterGeneration,
+ mMostFilteredDescendantGeneration,
+ mMarkedDirtyGeneration;
+
+ child_list_t mChildren;
+ LLFolderViewModelItem* mParent;
+ LLFolderViewModelInterface& mRootViewModel;
+
+ void setFolderViewItem(LLFolderViewItem* folder_view_item) { mFolderViewItem = folder_view_item;}
+ LLFolderViewItem* mFolderViewItem;
+};
+
+
+
+class LLFolderViewModelCommon : public LLFolderViewModelInterface
+{
+public:
+ LLFolderViewModelCommon()
+ : mTargetSortVersion(0),
+ mFolderView(NULL)
+ {}
+
+ virtual void requestSortAll()
+ {
+ // sort everything
+ mTargetSortVersion++;
+ }
+ virtual std::string getStatusText();
+ virtual void filter();
+
+ void setFolderView(LLFolderView* folder_view) { mFolderView = folder_view;}
+
+protected:
+ bool needsSort(class LLFolderViewModelItem* item);
+
+ S32 mTargetSortVersion;
+ LLFolderView* mFolderView;
+
+};
+
+template <typename SORT_TYPE, typename ITEM_TYPE, typename FOLDER_TYPE, typename FILTER_TYPE>
+class LLFolderViewModel : public LLFolderViewModelCommon
+{
+public:
+ typedef SORT_TYPE SortType;
+ typedef ITEM_TYPE ItemType;
+ typedef FOLDER_TYPE FolderType;
+ typedef FILTER_TYPE FilterType;
+
+ LLFolderViewModel(SortType* sorter, FilterType* filter)
+ : mSorter(sorter),
+ mFilter(filter)
+ {}
+
+ virtual ~LLFolderViewModel()
+ {
+ delete mSorter;
+ mSorter = NULL;
+ delete mFilter;
+ mFilter = NULL;
+ }
+
+ virtual SortType& getSorter() { return *mSorter; }
+ virtual const SortType& getSorter() const { return *mSorter; }
+ virtual void setSorter(const SortType& sorter) { mSorter = new SortType(sorter); requestSortAll(); }
+
+ virtual FilterType& getFilter() { return *mFilter; }
+ virtual const FilterType& getFilter() const { return *mFilter; }
+ virtual void setFilter(const FilterType& filter) { mFilter = new FilterType(filter); }
+
+ // By default, we assume the content is available. If a network fetch mechanism is implemented for the model,
+ // this method needs to be overloaded and return the relevant fetch status.
+ virtual bool contentsReady() { return true; }
+
+ struct ViewModelCompare
+ {
+ ViewModelCompare(const SortType& sorter)
+ : mSorter(sorter)
+ {}
+
+ bool operator () (const LLFolderViewItem* a, const LLFolderViewItem* b) const
+ {
+ return mSorter(static_cast<const ItemType*>(a->getViewModelItem()), static_cast<const ItemType*>(b->getViewModelItem()));
+ }
+
+ bool operator () (const LLFolderViewFolder* a, const LLFolderViewFolder* b) const
+ {
+ return mSorter(static_cast<const ItemType*>(a->getViewModelItem()), static_cast<const ItemType*>(b->getViewModelItem()));
+ }
+
+ const SortType& mSorter;
+ };
+
+ void sort(LLFolderViewFolder* folder)
+ {
+ if (needsSort(folder->getViewModelItem()))
+ {
+ folder->sortFolders(ViewModelCompare(getSorter()));
+ folder->sortItems(ViewModelCompare(getSorter()));
+ folder->getViewModelItem()->setSortVersion(mTargetSortVersion);
+ folder->requestArrange();
+ }
+ }
+
+protected:
+ SortType* mSorter;
+ FilterType* mFilter;
+};
+
+#endif // LLFOLDERVIEWMODEL_H
diff --git a/indra/llui/llfunctorregistry.h b/indra/llui/llfunctorregistry.h
index 752c7df7ee..f5364f4863 100644..100755
--- a/indra/llui/llfunctorregistry.h
+++ b/indra/llui/llfunctorregistry.h
@@ -69,14 +69,13 @@ public:
bool registerFunctor(const std::string& name, ResponseFunctor f)
{
bool retval = true;
- typename FunctorMap::iterator it = mMap.find(name);
if (mMap.count(name) == 0)
{
mMap[name] = f;
}
else
{
- llerrs << "attempt to store duplicate name '" << name << "' in LLFunctorRegistry. NOT ADDED." << llendl;
+ LL_ERRS() << "attempt to store duplicate name '" << name << "' in LLFunctorRegistry. NOT ADDED." << LL_ENDL;
retval = false;
}
@@ -87,7 +86,7 @@ public:
{
if (mMap.count(name) == 0)
{
- llwarns << "trying to remove '" << name << "' from LLFunctorRegistry but it's not there." << llendl;
+ LL_WARNS() << "trying to remove '" << name << "' from LLFunctorRegistry but it's not there." << LL_ENDL;
return false;
}
mMap.erase(name);
@@ -96,14 +95,13 @@ public:
FUNCTOR_TYPE getFunctor(const std::string& name)
{
- typename FunctorMap::iterator it = mMap.find(name);
if (mMap.count(name) != 0)
{
return mMap[name];
}
else
{
- llwarns << "tried to find '" << name << "' in LLFunctorRegistry, but it wasn't there." << llendl;
+ LL_DEBUGS() << "tried to find '" << name << "' in LLFunctorRegistry, but it wasn't there." << LL_ENDL;
return mMap[LOGFUNCTOR];
}
}
@@ -115,7 +113,7 @@ private:
static void log_functor(const LLSD& notification, const LLSD& payload)
{
- llwarns << "log_functor called with payload: " << payload << llendl;
+ LL_DEBUGS() << "log_functor called with payload: " << payload << LL_ENDL;
}
static void do_nothing(const LLSD& notification, const LLSD& payload)
diff --git a/indra/llui/llhandle.h b/indra/llui/llhandle.h
deleted file mode 100644
index 8c000eee48..0000000000
--- a/indra/llui/llhandle.h
+++ /dev/null
@@ -1,164 +0,0 @@
-/**
-* @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;
- }
-
- 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:
- 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
index 83317bd03c..1726347a78 100644..100755
--- a/indra/llui/llhelp.h
+++ b/indra/llui/llhelp.h
@@ -32,6 +32,7 @@ class LLHelp
{
public:
virtual void showTopic(const std::string &topic) = 0;
+ virtual std::string getURL(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
diff --git a/indra/llui/lliconctrl.cpp b/indra/llui/lliconctrl.cpp
index 47f2cfaf89..58b66f60ca 100644..100755
--- a/indra/llui/lliconctrl.cpp
+++ b/indra/llui/lliconctrl.cpp
@@ -42,11 +42,10 @@ LLIconCtrl::Params::Params()
: image("image_name"),
color("color"),
use_draw_context_alpha("use_draw_context_alpha", true),
- scale_image("scale_image")
-{
- tab_stop = false;
- mouse_opaque = false;
-}
+ scale_image("scale_image"),
+ min_width("min_width", 0),
+ min_height("min_height", 0)
+{}
LLIconCtrl::LLIconCtrl(const LLIconCtrl::Params& p)
: LLUICtrl(p),
@@ -54,8 +53,8 @@ LLIconCtrl::LLIconCtrl(const LLIconCtrl::Params& p)
mImagep(p.image),
mUseDrawContextAlpha(p.use_draw_context_alpha),
mPriority(0),
- mDrawWidth(0),
- mDrawHeight(0)
+ mMinWidth(p.min_width),
+ mMinHeight(p.min_height)
{
if (mImagep.notNull())
{
@@ -100,7 +99,13 @@ void LLIconCtrl::setValue(const LLSD& value )
mImagep = LLUI::getUIImage(tvalue.asString(), mPriority);
}
- setIconImageDrawSize();
+ if(mImagep.notNull()
+ && mImagep->getImage().notNull()
+ && mMinWidth
+ && mMinHeight)
+ {
+ mImagep->getImage()->setKnownDrawSize(llmax(mMinWidth, mImagep->getWidth()), llmax(mMinHeight, mImagep->getHeight()));
+ }
}
std::string LLIconCtrl::getImageName() const
@@ -111,14 +116,4 @@ std::string LLIconCtrl::getImageName() const
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 e9bdab2d47..8b1092df46 100644..100755
--- a/indra/llui/lliconctrl.h
+++ b/indra/llui/lliconctrl.h
@@ -30,7 +30,7 @@
#include "lluuid.h"
#include "v4color.h"
#include "lluictrl.h"
-#include "stdenums.h"
+#include "lluiimage.h"
class LLTextBox;
class LLUICtrlFactory;
@@ -49,7 +49,10 @@ public:
Optional<LLUIImage*> image;
Optional<LLUIColor> color;
Optional<bool> use_draw_context_alpha;
+ Optional<S32> min_width,
+ min_height;
Ignored scale_image;
+
Params();
};
protected:
@@ -69,16 +72,14 @@ public:
void setColor(const LLColor4& color) { mColor = color; }
void setImage(LLPointer<LLUIImage> image) { mImagep = image; }
+ const LLPointer<LLUIImage> getImage() { return mImagep; }
-private:
- void setIconImageDrawSize() ;
-
protected:
S32 mPriority;
//the output size of the icon image if set.
- S32 mDrawWidth ;
- S32 mDrawHeight ;
+ S32 mMinWidth,
+ mMinHeight;
// If set to true (default), use the draw context transparency.
// If false, will use transparency returned by getCurrentTransparency(). See STORM-698.
diff --git a/indra/llui/llkeywords.cpp b/indra/llui/llkeywords.cpp
index ceec9c7eb1..6750ee482a 100644..100755
--- a/indra/llui/llkeywords.cpp
+++ b/indra/llui/llkeywords.cpp
@@ -1,25 +1,25 @@
-/**
+/**
* @file llkeywords.cpp
* @brief Keyword list for LSL
*
* $LicenseInfo:firstyear=2000&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
- *
+ *
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
- *
+ *
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
- *
+ *
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
+ *
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -30,188 +30,359 @@
#include <fstream>
#include "llkeywords.h"
+#include "llsdserialize.h"
#include "lltexteditor.h"
#include "llstl.h"
-#include <boost/tokenizer.hpp>
-
-const U32 KEYWORD_FILE_CURRENT_VERSION = 2;
-inline BOOL LLKeywordToken::isHead(const llwchar* s) const
+inline bool LLKeywordToken::isHead(const llwchar* s) const
{
// strncmp is much faster than string compare
- BOOL res = TRUE;
+ bool res = true;
const llwchar* t = mToken.c_str();
S32 len = mToken.size();
for (S32 i=0; i<len; i++)
{
if (s[i] != t[i])
{
- res = FALSE;
+ res = false;
+ break;
+ }
+ }
+ return res;
+}
+
+inline bool LLKeywordToken::isTail(const llwchar* s) const
+{
+ bool res = true;
+ const llwchar* t = mDelimiter.c_str();
+ S32 len = mDelimiter.size();
+ for (S32 i=0; i<len; i++)
+ {
+ if (s[i] != t[i])
+ {
+ res = false;
break;
}
}
return res;
}
-LLKeywords::LLKeywords() : mLoaded(FALSE)
+LLKeywords::LLKeywords()
+: mLoaded(false)
{
}
LLKeywords::~LLKeywords()
{
std::for_each(mWordTokenMap.begin(), mWordTokenMap.end(), DeletePairedPointer());
+ mWordTokenMap.clear();
std::for_each(mLineTokenList.begin(), mLineTokenList.end(), DeletePointer());
+ mLineTokenList.clear();
std::for_each(mDelimiterTokenList.begin(), mDelimiterTokenList.end(), DeletePointer());
+ mDelimiterTokenList.clear();
}
-BOOL LLKeywords::loadFromFile( const std::string& filename )
+// Add the token as described
+void LLKeywords::addToken(LLKeywordToken::ETokenType type,
+ const std::string& key_in,
+ const LLColor4& color,
+ const std::string& tool_tip_in,
+ const std::string& delimiter_in)
{
- mLoaded = FALSE;
+ std::string tip_text = tool_tip_in;
+ LLStringUtil::replaceString(tip_text, "\\n", "\n" );
+ LLStringUtil::replaceString(tip_text, "\t", " " );
+ if (tip_text.empty())
+ {
+ tip_text = "[no info]";
+ }
+ LLWString tool_tip = utf8str_to_wstring(tip_text);
- ////////////////////////////////////////////////////////////
- // File header
+ LLWString key = utf8str_to_wstring(key_in);
+ LLWString delimiter = utf8str_to_wstring(delimiter_in);
+ switch(type)
+ {
+ case LLKeywordToken::TT_CONSTANT:
+ case LLKeywordToken::TT_CONTROL:
+ case LLKeywordToken::TT_EVENT:
+ case LLKeywordToken::TT_FUNCTION:
+ case LLKeywordToken::TT_LABEL:
+ case LLKeywordToken::TT_SECTION:
+ case LLKeywordToken::TT_TYPE:
+ case LLKeywordToken::TT_WORD:
+ mWordTokenMap[key] = new LLKeywordToken(type, color, key, tool_tip, LLWStringUtil::null);
+ break;
+
+ case LLKeywordToken::TT_LINE:
+ mLineTokenList.push_front(new LLKeywordToken(type, color, key, tool_tip, LLWStringUtil::null));
+ break;
- const S32 BUFFER_SIZE = 1024;
- char buffer[BUFFER_SIZE]; /* Flawfinder: ignore */
+ case LLKeywordToken::TT_TWO_SIDED_DELIMITER:
+ case LLKeywordToken::TT_DOUBLE_QUOTATION_MARKS:
+ case LLKeywordToken::TT_ONE_SIDED_DELIMITER:
+ mDelimiterTokenList.push_front(new LLKeywordToken(type, color, key, tool_tip, delimiter));
+ break;
- llifstream file;
- file.open(filename); /* Flawfinder: ignore */
- if( file.fail() )
- {
- llinfos << "LLKeywords::loadFromFile() Unable to open file: " << filename << llendl;
- return mLoaded;
+ default:
+ llassert(0);
}
+}
+
+std::string LLKeywords::getArguments(LLSD& arguments)
+{
+ std::string argString = "";
- // Identifying string
- file >> buffer;
- if( strcmp( buffer, "llkeywords" ) )
+ if (arguments.isArray())
{
- llinfos << filename << " does not appear to be a keyword file" << llendl;
- return mLoaded;
+ U32 argsCount = arguments.size();
+ LLSD::array_iterator arrayIt = arguments.beginArray();
+ for ( ; arrayIt != arguments.endArray(); ++arrayIt)
+ {
+ LLSD& args = (*arrayIt);
+ if (args.isMap())
+ {
+ LLSD::map_iterator argsIt = args.beginMap();
+ for ( ; argsIt != args.endMap(); ++argsIt)
+ {
+ argString += argsIt->second.get("type").asString() + " " + argsIt->first;
+ if (argsCount-- > 1)
+ {
+ argString += ", ";
+ }
+ }
+ }
+ else
+ {
+ LL_WARNS("SyntaxLSL") << "Argument array comtains a non-map element!" << LL_ENDL;
+ }
+ }
+ }
+ else if (!arguments.isUndefined())
+ {
+ LL_WARNS("SyntaxLSL") << "Not an array! Invalid arguments LLSD passed to function." << arguments << LL_ENDL;
}
+ return argString;
+}
- // Check file version
- file >> buffer;
- U32 version_num;
- file >> version_num;
- if( strcmp(buffer, "version") || version_num != (U32)KEYWORD_FILE_CURRENT_VERSION )
+std::string LLKeywords::getAttribute(const std::string& key)
+{
+ attribute_iterator_t it = mAttributes.find(key);
+ return (it != mAttributes.end()) ? it->second : "";
+}
+
+LLColor4 LLKeywords::getColorGroup(const std::string& key_in)
+{
+ std::string color_group = "ScriptText";
+ if (key_in == "functions")
+ {
+ color_group = "SyntaxLslFunction";
+ }
+ else if (key_in == "controls")
+ {
+ color_group = "SyntaxLslControlFlow";
+ }
+ else if (key_in == "events")
+ {
+ color_group = "SyntaxLslEvent";
+ }
+ else if (key_in == "types")
+ {
+ color_group = "SyntaxLslDataType";
+ }
+ else if (key_in == "misc-flow-label")
+ {
+ color_group = "SyntaxLslControlFlow";
+ }
+ else if (key_in =="deprecated")
+ {
+ color_group = "SyntaxLslDeprecated";
+ }
+ else if (key_in =="god-mode")
+ {
+ color_group = "SyntaxLslGodMode";
+ }
+ else if (key_in == "constants"
+ || key_in == "constants-integer"
+ || key_in == "constants-float"
+ || key_in == "constants-string"
+ || key_in == "constants-key"
+ || key_in == "constants-rotation"
+ || key_in == "constants-vector")
+ {
+ color_group = "SyntaxLslConstant";
+ }
+ else
{
- llinfos << filename << " does not appear to be a version " << KEYWORD_FILE_CURRENT_VERSION << " keyword file" << llendl;
- return mLoaded;
+ LL_WARNS("SyntaxLSL") << "Color key '" << key_in << "' not recognized." << LL_ENDL;
}
- // start of line (SOL)
- std::string SOL_COMMENT("#");
- std::string SOL_WORD("[word ");
- std::string SOL_LINE("[line ");
- std::string SOL_ONE_SIDED_DELIMITER("[one_sided_delimiter ");
- std::string SOL_TWO_SIDED_DELIMITER("[two_sided_delimiter ");
+ return LLUIColorTable::instance().getColor(color_group);
+}
- LLColor3 cur_color( 1, 0, 0 );
- LLKeywordToken::TOKEN_TYPE cur_type = LLKeywordToken::WORD;
+void LLKeywords::initialize(LLSD SyntaxXML)
+{
+ mSyntax = SyntaxXML;
+ mLoaded = true;
+}
- while (!file.eof())
+void LLKeywords::processTokens()
+{
+ if (!mLoaded)
{
- buffer[0] = 0;
- file.getline( buffer, BUFFER_SIZE );
- std::string line(buffer);
- if( line.find(SOL_COMMENT) == 0 )
- {
- continue;
- }
- else if( line.find(SOL_WORD) == 0 )
- {
- cur_color = readColor( line.substr(SOL_WORD.size()) );
- cur_type = LLKeywordToken::WORD;
- continue;
- }
- else if( line.find(SOL_LINE) == 0 )
- {
- cur_color = readColor( line.substr(SOL_LINE.size()) );
- cur_type = LLKeywordToken::LINE;
- continue;
- }
- else if( line.find(SOL_TWO_SIDED_DELIMITER) == 0 )
- {
- cur_color = readColor( line.substr(SOL_TWO_SIDED_DELIMITER.size()) );
- cur_type = LLKeywordToken::TWO_SIDED_DELIMITER;
- continue;
- }
- else if( line.find(SOL_ONE_SIDED_DELIMITER) == 0 )
- {
- cur_color = readColor( line.substr(SOL_ONE_SIDED_DELIMITER.size()) );
- cur_type = LLKeywordToken::ONE_SIDED_DELIMITER;
- continue;
- }
+ return;
+ }
- std::string token_buffer( line );
- LLStringUtil::trim(token_buffer);
-
- typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
- boost::char_separator<char> sep_word("", " \t");
- tokenizer word_tokens(token_buffer, sep_word);
- tokenizer::iterator token_word_iter = word_tokens.begin();
+ // Add 'standard' stuff: Quotes, Comments, Strings, Labels, etc. before processing the LLSD
+ std::string delimiter;
+ addToken(LLKeywordToken::TT_LABEL, "@", getColorGroup("misc-flow-label"), "Label\nTarget for jump statement", delimiter );
+ addToken(LLKeywordToken::TT_ONE_SIDED_DELIMITER, "//", LLUIColorTable::instance().getColor("SyntaxLslComment"), "Comment (single-line)\nNon-functional commentary or disabled code", delimiter );
+ addToken(LLKeywordToken::TT_TWO_SIDED_DELIMITER, "/*", LLUIColorTable::instance().getColor("SyntaxLslComment"), "Comment (multi-line)\nNon-functional commentary or disabled code", "*/" );
+ addToken(LLKeywordToken::TT_DOUBLE_QUOTATION_MARKS, "\"", LLUIColorTable::instance().getColor("SyntaxLslStringLiteral"), "String literal", "\"" );
- if( !token_buffer.empty() && token_word_iter != word_tokens.end() )
+ LLSD::map_iterator itr = mSyntax.beginMap();
+ for ( ; itr != mSyntax.endMap(); ++itr)
+ {
+ if (itr->first == "llsd-lsl-syntax-version")
{
- // first word is keyword
- std::string keyword = (*token_word_iter);
- LLStringUtil::trim(keyword);
-
- // following words are tooltip
- std::string tool_tip;
- while (++token_word_iter != word_tokens.end())
- {
- tool_tip += (*token_word_iter);
- }
- LLStringUtil::trim(tool_tip);
-
- if( !tool_tip.empty() )
+ // Skip over version key.
+ }
+ else
+ {
+ if (itr->second.isMap())
{
- // Replace : with \n for multi-line tool tips.
- LLStringUtil::replaceChar( tool_tip, ':', '\n' );
- addToken(cur_type, keyword, cur_color, tool_tip );
+ processTokensGroup(itr->second, itr->first);
}
else
{
- addToken(cur_type, keyword, cur_color, LLStringUtil::null );
+ LL_WARNS("LSL-Tokens-Processing") << "Map for " + itr->first + " entries is missing! Ignoring." << LL_ENDL;
}
}
}
-
- file.close();
-
- mLoaded = TRUE;
- return mLoaded;
+ LL_INFOS("SyntaxLSL") << "Finished processing tokens." << LL_ENDL;
}
-// Add the token as described
-void LLKeywords::addToken(LLKeywordToken::TOKEN_TYPE type,
- const std::string& key_in,
- const LLColor3& color,
- const std::string& tool_tip_in )
+void LLKeywords::processTokensGroup(const LLSD& tokens, const std::string& group)
{
- LLWString key = utf8str_to_wstring(key_in);
- LLWString tool_tip = utf8str_to_wstring(tool_tip_in);
- switch(type)
+ LLColor4 color;
+ LLColor4 color_group;
+ LLColor4 color_deprecated = getColorGroup("deprecated");
+ LLColor4 color_god_mode = getColorGroup("god-mode");
+
+ LLKeywordToken::ETokenType token_type = LLKeywordToken::TT_UNKNOWN;
+ // If a new token type is added here, it must also be added to the 'addToken' method
+ if (group == "constants")
{
- case LLKeywordToken::WORD:
- mWordTokenMap[key] = new LLKeywordToken(type, color, key, tool_tip);
- break;
+ token_type = LLKeywordToken::TT_CONSTANT;
+ }
+ else if (group == "controls")
+ {
+ token_type = LLKeywordToken::TT_CONTROL;
+ }
+ else if (group == "events")
+ {
+ token_type = LLKeywordToken::TT_EVENT;
+ }
+ else if (group == "functions")
+ {
+ token_type = LLKeywordToken::TT_FUNCTION;
+ }
+ else if (group == "label")
+ {
+ token_type = LLKeywordToken::TT_LABEL;
+ }
+ else if (group == "types")
+ {
+ token_type = LLKeywordToken::TT_TYPE;
+ }
- case LLKeywordToken::LINE:
- mLineTokenList.push_front(new LLKeywordToken(type, color, key, tool_tip));
- break;
+ color_group = getColorGroup(group);
+ LL_INFOS("SyntaxLSL") << "Group: '" << group << "', using color: '" << color_group << "'" << LL_ENDL;
- case LLKeywordToken::TWO_SIDED_DELIMITER:
- case LLKeywordToken::ONE_SIDED_DELIMITER:
- mDelimiterTokenList.push_front(new LLKeywordToken(type, color, key, tool_tip));
- break;
+ if (tokens.isMap())
+ {
+ LLSD::map_const_iterator outer_itr = tokens.beginMap();
+ for ( ; outer_itr != tokens.endMap(); ++outer_itr )
+ {
+ if (outer_itr->second.isMap())
+ {
+ mAttributes.clear();
+ LLSD arguments = LLSD();
+ LLSD::map_const_iterator inner_itr = outer_itr->second.beginMap();
+ for ( ; inner_itr != outer_itr->second.endMap(); ++inner_itr )
+ {
+ if (inner_itr->first == "arguments")
+ {
+ if (inner_itr->second.isArray())
+ {
+ arguments = inner_itr->second;
+ }
+ }
+ else if (!inner_itr->second.isMap() && !inner_itr->second.isArray())
+ {
+ mAttributes[inner_itr->first] = inner_itr->second.asString();
+ }
+ else
+ {
+ LL_WARNS("SyntaxLSL") << "Not a valid attribute: " << inner_itr->first << LL_ENDL;
+ }
+ }
- default:
- llassert(0);
+ std::string tooltip = "";
+ switch (token_type)
+ {
+ case LLKeywordToken::TT_CONSTANT:
+ color_group = getColorGroup(group + "-" + getAttribute("type"));
+ tooltip = "Type: " + getAttribute("type") + ", Value: " + getAttribute("value");
+ break;
+ case LLKeywordToken::TT_EVENT:
+ tooltip = outer_itr->first + "(" + getArguments(arguments) + ")";
+ break;
+ case LLKeywordToken::TT_FUNCTION:
+ tooltip = getAttribute("return") + " " + outer_itr->first + "(" + getArguments(arguments) + ");";
+ tooltip.append("\nEnergy: ");
+ tooltip.append(getAttribute("energy").empty() ? "0.0" : getAttribute("energy"));
+ if (!getAttribute("sleep").empty())
+ {
+ tooltip += ", Sleep: " + getAttribute("sleep");
+ }
+ default:
+ break;
+ }
+
+ if (!getAttribute("tooltip").empty())
+ {
+ if (!tooltip.empty())
+ {
+ tooltip.append("\n");
+ }
+ tooltip.append(getAttribute("tooltip"));
+ }
+
+ color = getAttribute("deprecated") == "true" ? color_deprecated : color_group;
+
+ if (getAttribute("god-mode") == "true")
+ {
+ color = color_god_mode;
+ }
+
+ addToken(token_type, outer_itr->first, color, tooltip);
+ }
+ }
+ }
+ else if (tokens.isArray()) // Currently nothing should need this, but it's here for completeness
+ {
+ LL_INFOS("SyntaxLSL") << "Curious, shouldn't be an array here; adding all using color " << color << LL_ENDL;
+ for (S32 count = 0; count < tokens.size(); ++count)
+ {
+ addToken(token_type, tokens[count], color, "");
+ }
+ }
+ else
+ {
+ LL_WARNS("SyntaxLSL") << "Invalid map/array passed: '" << tokens << "'" << LL_ENDL;
}
}
+
LLKeywords::WStringMapIndex::WStringMapIndex(const WStringMapIndex& other)
{
if(other.mOwner)
@@ -231,15 +402,19 @@ 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(const llwchar *start, size_t length)
+: mData(start)
+, mLength(length)
+, mOwner(false)
{
}
LLKeywords::WStringMapIndex::~WStringMapIndex()
{
- if(mOwner)
+ if (mOwner)
+ {
delete[] mData;
+ }
}
void LLKeywords::WStringMapIndex::copyData(const llwchar *start, size_t length)
@@ -256,13 +431,13 @@ bool LLKeywords::WStringMapIndex::operator<(const LLKeywords::WStringMapIndex &o
{
// 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)
@@ -277,7 +452,7 @@ bool LLKeywords::WStringMapIndex::operator<(const LLKeywords::WStringMapIndex &o
{
// self is shorter than other.
result = true;
- break;
+ break;
}
else if(*self_iter != *other_iter)
{
@@ -289,44 +464,30 @@ bool LLKeywords::WStringMapIndex::operator<(const LLKeywords::WStringMapIndex &o
self_iter++;
other_iter++;
}
-
- return result;
-}
-LLColor3 LLKeywords::readColor( const std::string& s )
-{
- F32 r, g, b;
- r = g = b = 0.0f;
- S32 values_read = sscanf(s.c_str(), "%f, %f, %f]", &r, &g, &b );
- if( values_read != 3 )
- {
- llinfos << " poorly formed color in keyword file" << llendl;
- }
- return LLColor3( r, g, b );
+ return result;
}
-LLFastTimer::DeclareTimer FTM_SYNTAX_COLORING("Syntax Coloring");
+LLTrace::BlockTimerStatHandle FTM_SYNTAX_COLORING("Syntax Coloring");
// Walk through a string, applying the rules specified by the keyword token list and
// create a list of color segments.
void LLKeywords::findSegments(std::vector<LLTextSegmentPtr>* seg_list, const LLWString& wtext, const LLColor4 &defaultColor, LLTextEditor& editor)
{
- LLFastTimer ft(FTM_SYNTAX_COLORING);
+ LL_RECORD_BLOCK_TIME(FTM_SYNTAX_COLORING);
seg_list->clear();
if( wtext.empty() )
{
return;
}
-
+
S32 text_len = wtext.size() + 1;
- seg_list->push_back( new LLNormalTextSegment( defaultColor, 0, text_len, editor ) );
+ seg_list->push_back( new LLNormalTextSegment( defaultColor, 0, text_len, editor ) );
const llwchar* base = wtext.c_str();
const llwchar* cur = base;
- const llwchar* line = NULL;
-
while( *cur )
{
if( *cur == '\n' || cur == base )
@@ -343,11 +504,8 @@ void LLKeywords::findSegments(std::vector<LLTextSegmentPtr>* seg_list, const LLW
}
}
- // Start of a new line
- line = cur;
-
// Skip white space
- while( *cur && isspace(*cur) && (*cur != '\n') )
+ while( *cur && iswspace(*cur) && (*cur != '\n') )
{
cur++;
}
@@ -356,8 +514,8 @@ void LLKeywords::findSegments(std::vector<LLTextSegmentPtr>* seg_list, const LLW
continue;
}
- // cur is now at the first non-whitespace character of a new line
-
+ // cur is now at the first non-whitespace character of a new line
+
// Line start tokens
{
BOOL line_done = FALSE;
@@ -374,7 +532,7 @@ void LLKeywords::findSegments(std::vector<LLTextSegmentPtr>* seg_list, const LLW
cur++;
}
S32 seg_end = cur - base;
-
+
//create segments from seg_start to seg_end
insertSegments(wtext, *seg_list,cur_token, text_len, seg_start, seg_end, defaultColor, editor);
line_done = TRUE; // to break out of second loop.
@@ -390,7 +548,7 @@ void LLKeywords::findSegments(std::vector<LLTextSegmentPtr>* seg_list, const LLW
}
// Skip white space
- while( *cur && isspace(*cur) && (*cur != '\n') )
+ while( *cur && iswspace(*cur) && (*cur != '\n') )
{
cur++;
}
@@ -418,14 +576,15 @@ void LLKeywords::findSegments(std::vector<LLTextSegmentPtr>* seg_list, const LLW
S32 seg_end = 0;
seg_start = cur - base;
- cur += cur_delimiter->getLength();
-
- if( cur_delimiter->getType() == LLKeywordToken::TWO_SIDED_DELIMITER )
+ cur += cur_delimiter->getLengthHead();
+
+ LLKeywordToken::ETokenType type = cur_delimiter->getType();
+ if( type == LLKeywordToken::TT_TWO_SIDED_DELIMITER || type == LLKeywordToken::TT_DOUBLE_QUOTATION_MARKS )
{
- while( *cur && !cur_delimiter->isHead(cur))
+ while( *cur && !cur_delimiter->isTail(cur))
{
// Check for an escape sequence.
- if (*cur == '\\')
+ if (type == LLKeywordToken::TT_DOUBLE_QUOTATION_MARKS && *cur == '\\')
{
// Count the number of backslashes.
S32 num_backslashes = 0;
@@ -435,10 +594,10 @@ void LLKeywords::findSegments(std::vector<LLTextSegmentPtr>* seg_list, const LLW
between_delimiters++;
cur++;
}
- // Is the next character the end delimiter?
- if (cur_delimiter->isHead(cur))
+ // If the next character is the end delimiter?
+ if (cur_delimiter->isTail(cur))
{
- // Is there was an odd number of backslashes, then this delimiter
+ // If there was an odd number of backslashes, then this delimiter
// does not end the sequence.
if (num_backslashes % 2 == 1)
{
@@ -461,25 +620,25 @@ void LLKeywords::findSegments(std::vector<LLTextSegmentPtr>* seg_list, const LLW
if( *cur )
{
- cur += cur_delimiter->getLength();
- seg_end = seg_start + between_delimiters + 2 * cur_delimiter->getLength();
+ cur += cur_delimiter->getLengthHead();
+ seg_end = seg_start + between_delimiters + cur_delimiter->getLengthHead() + cur_delimiter->getLengthTail();
}
else
{
// eof
- seg_end = seg_start + between_delimiters + cur_delimiter->getLength();
+ seg_end = seg_start + between_delimiters + cur_delimiter->getLengthHead();
}
}
else
{
- llassert( cur_delimiter->getType() == LLKeywordToken::ONE_SIDED_DELIMITER );
+ llassert( cur_delimiter->getType() == LLKeywordToken::TT_ONE_SIDED_DELIMITER );
// Left side is the delimiter. Right side is eol or eof.
while( *cur && ('\n' != *cur) )
{
between_delimiters++;
cur++;
}
- seg_end = seg_start + between_delimiters + cur_delimiter->getLength();
+ seg_end = seg_start + between_delimiters + cur_delimiter->getLengthHead();
}
insertSegments(wtext, *seg_list,cur_delimiter, text_len, seg_start, seg_end, defaultColor, editor);
@@ -496,10 +655,10 @@ void LLKeywords::findSegments(std::vector<LLTextSegmentPtr>* seg_list, const LLW
// check against words
llwchar prev = cur > base ? *(cur-1) : 0;
- if( !isalnum( prev ) && (prev != '_') )
+ if( !iswalnum( prev ) && (prev != '_') )
{
const llwchar* p = cur;
- while( isalnum( *p ) || (*p == '_') )
+ while( iswalnum( *p ) || (*p == '_') )
{
p++;
}
@@ -518,7 +677,7 @@ void LLKeywords::findSegments(std::vector<LLTextSegmentPtr>* seg_list, const LLW
insertSegments(wtext, *seg_list,cur_token, text_len, seg_start, seg_end, defaultColor, editor);
}
- cur += seg_len;
+ cur += seg_len;
continue;
}
}
@@ -534,7 +693,7 @@ void LLKeywords::findSegments(std::vector<LLTextSegmentPtr>* seg_list, const LLW
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)
@@ -613,7 +772,7 @@ void LLKeywords::dump()
void LLKeywordToken::dump()
{
- llinfos << "[" <<
+ llinfos << "[" <<
mColor.mV[VX] << ", " <<
mColor.mV[VY] << ", " <<
mColor.mV[VZ] << "] [" <<
diff --git a/indra/llui/llkeywords.h b/indra/llui/llkeywords.h
index f6d75b7e75..18e2ed06c5 100644..100755
--- a/indra/llui/llkeywords.h
+++ b/indra/llui/llkeywords.h
@@ -1,25 +1,25 @@
-/**
+/**
* @file llkeywords.h
* @brief Keyword list for LSL
*
* $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$
*/
@@ -28,8 +28,10 @@
#define LL_LLKEYWORDS_H
+#include "lldir.h"
#include "llstring.h"
#include "v3color.h"
+#include "v4color.h"
#include <map>
#include <list>
#include <deque>
@@ -41,33 +43,64 @@ typedef LLPointer<LLTextSegment> LLTextSegmentPtr;
class LLKeywordToken
{
public:
- enum TOKEN_TYPE { WORD, LINE, TWO_SIDED_DELIMITER, ONE_SIDED_DELIMITER };
-
- LLKeywordToken( TOKEN_TYPE type, const LLColor3& color, const LLWString& token, const LLWString& tool_tip )
+ /**
+ * @brief Types of tokens/delimters being parsed.
+ *
+ * @desc Tokens/delimiters that need to be identified/highlighted. All are terminated if an EOF is encountered.
+ * - TT_WORD are keywords in the normal sense, i.e. constants, events, etc.
+ * - TT_LINE are for entire lines (currently only flow control labels use this).
+ * - TT_ONE_SIDED_DELIMITER are for open-ended delimiters which are terminated by EOL.
+ * - TT_TWO_SIDED_DELIMITER are for delimiters that end with a different delimiter than they open with.
+ * - TT_DOUBLE_QUOTATION_MARKS are for delimiting areas using the same delimiter to open and close.
+ */
+ typedef enum e_token_type
+ {
+ TT_UNKNOWN,
+ TT_WORD,
+ TT_LINE,
+ TT_TWO_SIDED_DELIMITER,
+ TT_ONE_SIDED_DELIMITER,
+ TT_DOUBLE_QUOTATION_MARKS,
+ // Following constants are more specific versions of the preceding ones
+ TT_CONSTANT, // WORD
+ TT_CONTROL, // WORD
+ TT_EVENT, // WORD
+ TT_FUNCTION, // WORD
+ TT_LABEL, // LINE
+ TT_SECTION, // WORD
+ TT_TYPE // WORD
+ } ETokenType;
+
+ LLKeywordToken( ETokenType type, const LLColor4& color, const LLWString& token, const LLWString& tool_tip, const LLWString& delimiter )
:
mType( type ),
mToken( token ),
mColor( color ),
- mToolTip( tool_tip )
+ mToolTip( tool_tip ),
+ mDelimiter( delimiter ) // right delimiter
{
}
- S32 getLength() const { return mToken.size(); }
- BOOL isHead(const llwchar* s) const;
+ S32 getLengthHead() const { return mToken.size(); }
+ S32 getLengthTail() const { return mDelimiter.size(); }
+ bool isHead(const llwchar* s) const;
+ bool isTail(const llwchar* s) const;
const LLWString& getToken() const { return mToken; }
- const LLColor3& getColor() const { return mColor; }
- TOKEN_TYPE getType() const { return mType; }
+ const LLColor4& getColor() const { return mColor; }
+ ETokenType getType() const { return mType; }
const LLWString& getToolTip() const { return mToolTip; }
+ const LLWString& getDelimiter() const { return mDelimiter; }
#ifdef _DEBUG
void dump();
#endif
private:
- TOKEN_TYPE mType;
+ ETokenType mType;
LLWString mToken;
- LLColor3 mColor;
+ LLColor4 mColor;
LLWString mToolTip;
+ LLWString mDelimiter;
};
class LLKeywords
@@ -76,17 +109,24 @@ public:
LLKeywords();
~LLKeywords();
- BOOL loadFromFile(const std::string& filename);
- BOOL isLoaded() const { return mLoaded; }
+ void clearLoaded() { mLoaded = false; }
+ LLColor4 getColorGroup(const std::string& key_in);
+ bool isLoaded() const { return mLoaded; }
- void findSegments(std::vector<LLTextSegmentPtr> *seg_list, const LLWString& text, const LLColor4 &defaultColor, class LLTextEditor& editor );
+ void findSegments(std::vector<LLTextSegmentPtr> *seg_list,
+ const LLWString& text,
+ const LLColor4 &defaultColor,
+ class LLTextEditor& editor);
+ void initialize(LLSD SyntaxXML);
+ void processTokens();
// Add the token as described
- void addToken(LLKeywordToken::TOKEN_TYPE type,
+ void addToken(LLKeywordToken::ETokenType type,
const std::string& key,
- const LLColor3& color,
- const std::string& tool_tip = LLStringUtil::null);
-
+ const LLColor4& color,
+ const std::string& tool_tip = LLStringUtil::null,
+ const std::string& delimiter = LLStringUtil::null);
+
// 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
@@ -110,6 +150,9 @@ public:
const llwchar *mData;
size_t mLength;
bool mOwner;
+
+
+ LLColor4 mColor;
};
typedef std::map<WStringMapIndex, LLKeywordToken*> word_token_map_t;
@@ -117,20 +160,43 @@ public:
keyword_iterator_t begin() const { return mWordTokenMap.begin(); }
keyword_iterator_t end() const { return mWordTokenMap.end(); }
+ typedef std::map<WStringMapIndex, LLColor4> group_color_map_t;
+ typedef group_color_map_t::const_iterator color_iterator_t;
+ group_color_map_t mColorGroupMap;
+
#ifdef _DEBUG
void dump();
#endif
-private:
- LLColor3 readColor(const std::string& s);
- 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;
+protected:
+ void processTokensGroup(const LLSD& Tokens, const std::string& Group);
+ 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;
+ LLSD mSyntax;
word_token_map_t mWordTokenMap;
typedef std::deque<LLKeywordToken*> token_list_t;
token_list_t mLineTokenList;
token_list_t mDelimiterTokenList;
+
+ typedef std::map<std::string, std::string> element_attributes_t;
+ typedef element_attributes_t::const_iterator attribute_iterator_t;
+ element_attributes_t mAttributes;
+ std::string getAttribute(const std::string& key);
+
+ std::string getArguments(LLSD& arguments);
};
#endif // LL_LLKEYWORDS_H
diff --git a/indra/llui/lllayoutstack.cpp b/indra/llui/lllayoutstack.cpp
index 19ac4c58a8..69246a2f57 100644..100755
--- a/indra/llui/lllayoutstack.cpp
+++ b/indra/llui/lllayoutstack.cpp
@@ -32,31 +32,43 @@
#include "lllocalcliprect.h"
#include "llpanel.h"
-#include "llresizebar.h"
#include "llcriticaldamp.h"
+#include "lliconctrl.h"
+#include "boost/foreach.hpp"
+
+static const F32 MIN_FRACTIONAL_SIZE = 0.00001f;
+static const F32 MAX_FRACTIONAL_SIZE = 1.f;
static LLDefaultChildRegistry::Register<LLLayoutStack> register_layout_stack("layout_stack");
static LLLayoutStack::LayoutStackRegistry::Register<LLLayoutPanel> register_layout_panel("layout_panel");
-void LLLayoutStack::OrientationNames::declareValues()
-{
- declare("horizontal", HORIZONTAL);
- declare("vertical", VERTICAL);
-}
-
//
// LLLayoutPanel
//
+LLLayoutPanel::Params::Params()
+: expanded_min_dim("expanded_min_dim", 0),
+ min_dim("min_dim", -1),
+ user_resize("user_resize", false),
+ auto_resize("auto_resize", true)
+{
+ addSynonym(min_dim, "min_width");
+ addSynonym(min_dim, "min_height");
+}
+
LLLayoutPanel::LLLayoutPanel(const Params& p)
: LLPanel(p),
+ mExpandedMinDim(p.expanded_min_dim.isProvided() ? p.expanded_min_dim : p.min_dim),
mMinDim(p.min_dim),
- mMaxDim(p.max_dim),
mAutoResize(p.auto_resize),
mUserResize(p.user_resize),
mCollapsed(FALSE),
mCollapseAmt(0.f),
mVisibleAmt(1.f), // default to fully visible
- mResizeBar(NULL)
+ mResizeBar(NULL),
+ mFractionalSize(0.f),
+ mTargetDim(0),
+ mIgnoreReshape(false),
+ mOrientation(LLLayoutStack::HORIZONTAL)
{
// panels initialized as hidden should not start out partially visible
if (!getVisible())
@@ -78,21 +90,112 @@ LLLayoutPanel::~LLLayoutPanel()
delete mResizeBar;
mResizeBar = NULL;
}
-
-F32 LLLayoutPanel::getCollapseFactor(LLLayoutStack::ELayoutOrientation orientation)
+
+F32 LLLayoutPanel::getAutoResizeFactor() const
+{
+ return mVisibleAmt * (1.f - mCollapseAmt);
+}
+
+F32 LLLayoutPanel::getVisibleAmount() const
+{
+ return mVisibleAmt;
+}
+
+S32 LLLayoutPanel::getLayoutDim() const
+{
+ return ll_round((F32)((mOrientation == LLLayoutStack::HORIZONTAL)
+ ? getRect().getWidth()
+ : getRect().getHeight()));
+}
+
+S32 LLLayoutPanel::getTargetDim() const
+{
+ return mTargetDim;
+}
+
+void LLLayoutPanel::setTargetDim(S32 value)
{
- if (orientation == LLLayoutStack::HORIZONTAL)
+ LLRect new_rect(getRect());
+ if (mOrientation == LLLayoutStack::HORIZONTAL)
{
- F32 collapse_amt =
- clamp_rescale(mCollapseAmt, 0.f, 1.f, 1.f, (F32)mMinDim / (F32)llmax(1, getRect().getWidth()));
- return mVisibleAmt * collapse_amt;
+ new_rect.mRight = new_rect.mLeft + value;
}
else
{
- F32 collapse_amt =
- clamp_rescale(mCollapseAmt, 0.f, 1.f, 1.f, llmin(1.f, (F32)mMinDim / (F32)llmax(1, getRect().getHeight())));
- return mVisibleAmt * collapse_amt;
+ new_rect.mTop = new_rect.mBottom + value;
+ }
+ setShape(new_rect, true);
+}
+
+S32 LLLayoutPanel::getVisibleDim() const
+{
+ F32 min_dim = getRelevantMinDim();
+ return ll_round(mVisibleAmt
+ * (min_dim
+ + (((F32)mTargetDim - min_dim) * (1.f - mCollapseAmt))));
+}
+
+void LLLayoutPanel::setOrientation( LLView::EOrientation orientation )
+{
+ mOrientation = orientation;
+ S32 layout_dim = ll_round((F32)((mOrientation == LLLayoutStack::HORIZONTAL)
+ ? getRect().getWidth()
+ : getRect().getHeight()));
+
+ if (mAutoResize == FALSE
+ && mUserResize == TRUE
+ && mMinDim == -1 )
+ {
+ setMinDim(layout_dim);
+ }
+ mTargetDim = llmax(layout_dim, getMinDim());
+}
+
+void LLLayoutPanel::setVisible( BOOL visible )
+{
+ if (visible != getVisible())
+ {
+ LLLayoutStack* stackp = dynamic_cast<LLLayoutStack*>(getParent());
+ if (stackp)
+ {
+ stackp->mNeedsLayout = true;
+ }
+ }
+ LLPanel::setVisible(visible);
+}
+
+void LLLayoutPanel::reshape( S32 width, S32 height, BOOL called_from_parent /*= TRUE*/ )
+{
+ if (width == getRect().getWidth() && height == getRect().getHeight()) return;
+
+ if (!mIgnoreReshape && mAutoResize == false)
+ {
+ mTargetDim = (mOrientation == LLLayoutStack::HORIZONTAL) ? width : height;
+ LLLayoutStack* stackp = dynamic_cast<LLLayoutStack*>(getParent());
+ if (stackp)
+ {
+ stackp->mNeedsLayout = true;
+ }
+ }
+ LLPanel::reshape(width, height, called_from_parent);
+}
+
+void LLLayoutPanel::handleReshape(const LLRect& new_rect, bool by_user)
+{
+ LLLayoutStack* stackp = dynamic_cast<LLLayoutStack*>(getParent());
+ if (stackp)
+ {
+ if (by_user)
+ { // tell layout stack to account for new shape
+
+ // make sure that panels have already been auto resized
+ stackp->updateLayout();
+ // now apply requested size to panel
+ stackp->updatePanelRect(this, new_rect);
+ }
+ stackp->mNeedsLayout = true;
}
+ LLPanel::handleReshape(new_rect, by_user);
}
//
@@ -105,23 +208,35 @@ LLLayoutStack::Params::Params()
clip("clip", true),
open_time_constant("open_time_constant", 0.02f),
close_time_constant("close_time_constant", 0.03f),
- border_size("border_size", LLCachedControl<S32>(*LLUI::sSettingGroups["config"], "UIResizeBarHeight", 0))
+ resize_bar_overlap("resize_bar_overlap", 1),
+ border_size("border_size", LLCachedControl<S32>(*LLUI::sSettingGroups["config"], "UIResizeBarHeight", 0)),
+ show_drag_handle("show_drag_handle", false),
+ drag_handle_first_indent("drag_handle_first_indent", 0),
+ drag_handle_second_indent("drag_handle_second_indent", 0),
+ drag_handle_thickness("drag_handle_thickness", 5),
+ drag_handle_shift("drag_handle_shift", 2)
{
- name="stack";
+ addSynonym(border_size, "drag_handle_gap");
}
LLLayoutStack::LLLayoutStack(const LLLayoutStack::Params& p)
: LLView(p),
- mMinWidth(0),
- mMinHeight(0),
mPanelSpacing(p.border_size),
mOrientation(p.orientation),
mAnimate(p.animate),
mAnimatedThisFrame(false),
+ mNeedsLayout(true),
mClip(p.clip),
mOpenTimeConstant(p.open_time_constant),
- mCloseTimeConstant(p.close_time_constant)
-{}
+ mCloseTimeConstant(p.close_time_constant),
+ mResizeBarOverlap(p.resize_bar_overlap),
+ mShowDragHandle(p.show_drag_handle),
+ mDragHandleFirstIndent(p.drag_handle_first_indent),
+ mDragHandleSecondIndent(p.drag_handle_second_indent),
+ mDragHandleThickness(p.drag_handle_thickness),
+ mDragHandleShift(p.drag_handle_shift)
+{
+}
LLLayoutStack::~LLLayoutStack()
{
@@ -134,28 +249,31 @@ void LLLayoutStack::draw()
{
updateLayout();
- e_panel_list_t::iterator panel_it;
- for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it)
+ // always clip to stack itself
+ LLLocalClipRect clip(getLocalRect());
+ BOOST_FOREACH(LLLayoutPanel* panelp, mPanels)
{
// clip to layout rectangle, not bounding rectangle
- LLRect clip_rect = (*panel_it)->getRect();
+ LLRect clip_rect = panelp->getRect();
// scale clipping rectangle by visible amount
if (mOrientation == HORIZONTAL)
{
- clip_rect.mRight = clip_rect.mLeft + llround((F32)clip_rect.getWidth() * (*panel_it)->getCollapseFactor(mOrientation));
+ clip_rect.mRight = clip_rect.mLeft + panelp->getVisibleDim();
}
else
{
- clip_rect.mBottom = clip_rect.mTop - llround((F32)clip_rect.getHeight() * (*panel_it)->getCollapseFactor(mOrientation));
+ clip_rect.mBottom = clip_rect.mTop - panelp->getVisibleDim();
}
- LLPanel* panelp = (*panel_it);
-
- LLLocalClipRect clip(clip_rect, mClip);
- // only force drawing invisible children if visible amount is non-zero
- drawChild(panelp, 0, 0, !clip_rect.isEmpty());
+ {LLLocalClipRect clip(clip_rect, mClip);
+ // only force drawing invisible children if visible amount is non-zero
+ drawChild(panelp, 0, 0, !clip_rect.isEmpty());
+ }
+ if (panelp->getResizeBar()->getVisible())
+ {
+ drawChild(panelp->getResizeBar());
+ }
}
- mAnimatedThisFrame = false;
}
void LLLayoutStack::removeChild(LLView* view)
@@ -166,12 +284,10 @@ void LLLayoutStack::removeChild(LLView* view)
{
mPanels.erase(std::find(mPanels.begin(), mPanels.end(), embedded_panelp));
delete embedded_panelp;
+ updateFractionalSizes();
+ mNeedsLayout = true;
}
- // need to update resizebars
-
- calcMinExtents();
-
LLView::removeChild(view);
}
@@ -182,54 +298,19 @@ BOOL LLLayoutStack::postBuild()
}
bool LLLayoutStack::addChild(LLView* child, S32 tab_group)
- {
- LLLayoutPanel* panelp = dynamic_cast<LLLayoutPanel*>(child);
- if (panelp)
- {
- mPanels.push_back(panelp);
- }
- return LLView::addChild(child, tab_group);
- }
-
-
-S32 LLLayoutStack::getDefaultHeight(S32 cur_height)
-{
- // if we are spanning our children (crude upward propagation of size)
- // then don't enforce our size on our children
- if (mOrientation == HORIZONTAL)
- {
- cur_height = llmax(mMinHeight, getRect().getHeight());
- }
-
- return cur_height;
-}
-
-S32 LLLayoutStack::getDefaultWidth(S32 cur_width)
{
- // if we are spanning our children (crude upward propagation of size)
- // then don't enforce our size on our children
- if (mOrientation == VERTICAL)
+ LLLayoutPanel* panelp = dynamic_cast<LLLayoutPanel*>(child);
+ if (panelp)
{
- cur_width = llmax(mMinWidth, getRect().getWidth());
+ panelp->setOrientation(mOrientation);
+ mPanels.push_back(panelp);
+ createResizeBar(panelp);
+ mNeedsLayout = true;
}
+ BOOL result = LLView::addChild(child, tab_group);
- return cur_width;
-}
-
-void LLLayoutStack::movePanel(LLPanel* panel_to_move, LLPanel* target_panel, bool move_to_front)
-{
- LLLayoutPanel* embedded_panel_to_move = findEmbeddedPanel(panel_to_move);
- LLLayoutPanel* 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);
+ updateFractionalSizes();
+ return result;
}
void LLLayoutStack::addPanel(LLLayoutPanel* panel, EAnimate animate)
@@ -244,480 +325,670 @@ void LLLayoutStack::addPanel(LLLayoutPanel* panel, EAnimate animate)
}
}
-void LLLayoutStack::removePanel(LLPanel* panel)
-{
- removeChild(panel);
-}
-
void LLLayoutStack::collapsePanel(LLPanel* panel, BOOL collapsed)
{
LLLayoutPanel* panel_container = findEmbeddedPanel(panel);
if (!panel_container) return;
panel_container->mCollapsed = collapsed;
+ mNeedsLayout = true;
}
-void LLLayoutStack::updatePanelAutoResize(const std::string& panel_name, BOOL auto_resize)
-{
- LLLayoutPanel* panel = findEmbeddedPanelByName(panel_name);
-
- if (panel)
- {
- panel->mAutoResize = auto_resize;
- }
-}
+static LLTrace::BlockTimerStatHandle FTM_UPDATE_LAYOUT("Update LayoutStacks");
-void LLLayoutStack::setPanelUserResize(const std::string& panel_name, BOOL user_resize)
+class LLImagePanel : public LLPanel
{
- LLLayoutPanel* panel = findEmbeddedPanelByName(panel_name);
+public:
+ struct Params : public LLInitParam::Block<Params, LLPanel::Params>
+ {
+ Optional<bool> horizontal;
+ Params() : horizontal("horizontal", false) {}
+ };
+ LLImagePanel(const Params& p) : LLPanel(p), mHorizontal(p.horizontal) {}
+ virtual ~LLImagePanel() {}
- if (panel)
+ void draw()
{
- panel->mUserResize = user_resize;
+ const LLRect& parent_rect = getParent()->getRect();
+ const LLRect& rect = getRect();
+ LLRect clip_rect( -rect.mLeft, parent_rect.getHeight() - rect.mBottom - 2
+ , parent_rect.getWidth() - rect.mLeft - (mHorizontal ? 2 : 0), -rect.mBottom);
+ LLLocalClipRect clip(clip_rect);
+ LLPanel::draw();
}
-}
-bool LLLayoutStack::getPanelMinSize(const std::string& panel_name, S32* min_dimp)
-{
- LLLayoutPanel* panel = findEmbeddedPanelByName(panel_name);
+private:
+ bool mHorizontal;
+};
- if (panel)
- {
- if (min_dimp) *min_dimp = panel->mMinDim;
- }
+void LLLayoutStack::updateLayout()
+{
+ LL_RECORD_BLOCK_TIME(FTM_UPDATE_LAYOUT);
- return NULL != panel;
-}
+ if (!mNeedsLayout) return;
-bool LLLayoutStack::getPanelMaxSize(const std::string& panel_name, S32* max_dimp)
-{
- LLLayoutPanel* panel = findEmbeddedPanelByName(panel_name);
+ bool continue_animating = animatePanels();
+ F32 total_visible_fraction = 0.f;
+ S32 space_to_distribute = (mOrientation == HORIZONTAL)
+ ? getRect().getWidth()
+ : getRect().getHeight();
- if (panel)
+ // first, assign minimum dimensions
+ LLLayoutPanel* panelp = NULL;
+ BOOST_FOREACH(panelp, mPanels)
{
- if (max_dimp) *max_dimp = panel->mMaxDim;
+ if (panelp->mAutoResize)
+ {
+ panelp->mTargetDim = panelp->getRelevantMinDim();
+ }
+ space_to_distribute -= panelp->getVisibleDim() + ll_round((F32)mPanelSpacing * panelp->getVisibleAmount());
+ total_visible_fraction += panelp->mFractionalSize * panelp->getAutoResizeFactor();
}
- 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();
- createResizeBars();
+ llassert(total_visible_fraction < 1.05f);
- // calculate current extents
- S32 total_width = 0;
- S32 total_height = 0;
+ // don't need spacing after last panel
+ space_to_distribute += panelp ? ll_round((F32)mPanelSpacing * panelp->getVisibleAmount()) : 0;
- e_panel_list_t::iterator panel_it;
- for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it)
- {
- LLPanel* panelp = (*panel_it);
- if (panelp->getVisible())
+ S32 remaining_space = space_to_distribute;
+ F32 fraction_distributed = 0.f;
+ if (space_to_distribute > 0 && total_visible_fraction > 0.f)
+ { // give space proportionally to visible auto resize panels
+ BOOST_FOREACH(LLLayoutPanel* panelp, mPanels)
{
- if (mAnimate)
- {
- if (!mAnimatedThisFrame)
- {
- (*panel_it)->mVisibleAmt = lerp((*panel_it)->mVisibleAmt, 1.f, LLCriticalDamp::getInterpolant(mOpenTimeConstant));
- if ((*panel_it)->mVisibleAmt > 0.99f)
- {
- (*panel_it)->mVisibleAmt = 1.f;
- }
- }
- }
- else
+ if (panelp->mAutoResize)
{
- (*panel_it)->mVisibleAmt = 1.f;
+ F32 fraction_to_distribute = (panelp->mFractionalSize * panelp->getAutoResizeFactor()) / (total_visible_fraction);
+ S32 delta = ll_round((F32)space_to_distribute * fraction_to_distribute);
+ fraction_distributed += fraction_to_distribute;
+ panelp->mTargetDim += delta;
+ remaining_space -= delta;
}
}
- else // not visible
+ }
+
+ // distribute any left over pixels to non-collapsed, visible panels
+ BOOST_FOREACH(LLLayoutPanel* panelp, mPanels)
+ {
+ if (remaining_space == 0) break;
+
+ if (panelp->mAutoResize
+ && !panelp->mCollapsed
+ && panelp->getVisible())
{
- if (mAnimate)
- {
- if (!mAnimatedThisFrame)
- {
- (*panel_it)->mVisibleAmt = lerp((*panel_it)->mVisibleAmt, 0.f, LLCriticalDamp::getInterpolant(mCloseTimeConstant));
- if ((*panel_it)->mVisibleAmt < 0.001f)
- {
- (*panel_it)->mVisibleAmt = 0.f;
- }
- }
- }
- else
- {
- (*panel_it)->mVisibleAmt = 0.f;
- }
+ S32 space_for_panel = remaining_space > 0 ? 1 : -1;
+ panelp->mTargetDim += space_for_panel;
+ remaining_space -= space_for_panel;
}
+ }
+
+ F32 cur_pos = (mOrientation == HORIZONTAL) ? 0.f : (F32)getRect().getHeight();
- if ((*panel_it)->mCollapsed)
+ BOOST_FOREACH(LLLayoutPanel* panelp, mPanels)
+ {
+ F32 panel_dim = llmax(panelp->getExpandedMinDim(), panelp->mTargetDim);
+
+ LLRect panel_rect;
+ if (mOrientation == HORIZONTAL)
{
- (*panel_it)->mCollapseAmt = lerp((*panel_it)->mCollapseAmt, 1.f, LLCriticalDamp::getInterpolant(mCloseTimeConstant));
+ panel_rect.setLeftTopAndSize(ll_round(cur_pos),
+ getRect().getHeight(),
+ ll_round(panel_dim),
+ getRect().getHeight());
}
else
{
- (*panel_it)->mCollapseAmt = lerp((*panel_it)->mCollapseAmt, 0.f, LLCriticalDamp::getInterpolant(mCloseTimeConstant));
+ panel_rect.setLeftTopAndSize(0,
+ ll_round(cur_pos),
+ getRect().getWidth(),
+ ll_round(panel_dim));
}
+ LLRect resize_bar_rect(panel_rect);
+ F32 panel_spacing = (F32)mPanelSpacing * panelp->getVisibleAmount();
+ F32 panel_visible_dim = panelp->getVisibleDim();
+ S32 panel_spacing_round = (S32)(ll_round(panel_spacing));
+
if (mOrientation == HORIZONTAL)
{
- // enforce minimize size constraint by default
- if (panelp->getRect().getWidth() < (*panel_it)->mMinDim)
+ cur_pos += panel_visible_dim + panel_spacing;
+
+ if (mShowDragHandle && panel_spacing_round > mDragHandleThickness)
{
- panelp->reshape((*panel_it)->mMinDim, panelp->getRect().getHeight());
+ resize_bar_rect.mLeft = panel_rect.mRight + mDragHandleShift;
+ resize_bar_rect.mRight = resize_bar_rect.mLeft + mDragHandleThickness;
}
- total_width += llround(panelp->getRect().getWidth() * (*panel_it)->getCollapseFactor(mOrientation));
- // want n-1 panel gaps for n panels
- if (panel_it != mPanels.begin())
+ else
+ {
+ resize_bar_rect.mLeft = panel_rect.mRight - mResizeBarOverlap;
+ resize_bar_rect.mRight = panel_rect.mRight + panel_spacing_round + mResizeBarOverlap;
+ }
+
+ if (mShowDragHandle)
{
- total_width += mPanelSpacing;
+ resize_bar_rect.mBottom += mDragHandleSecondIndent;
+ resize_bar_rect.mTop -= mDragHandleFirstIndent;
}
+
}
else //VERTICAL
{
- // enforce minimize size constraint by default
- if (panelp->getRect().getHeight() < (*panel_it)->mMinDim)
+ cur_pos -= panel_visible_dim + panel_spacing;
+
+ if (mShowDragHandle && panel_spacing_round > mDragHandleThickness)
{
- panelp->reshape(panelp->getRect().getWidth(), (*panel_it)->mMinDim);
+ resize_bar_rect.mTop = panel_rect.mBottom - mDragHandleShift;
+ resize_bar_rect.mBottom = resize_bar_rect.mTop - mDragHandleThickness;
}
- total_height += llround(panelp->getRect().getHeight() * (*panel_it)->getCollapseFactor(mOrientation));
- if (panel_it != mPanels.begin())
+ else
+ {
+ resize_bar_rect.mTop = panel_rect.mBottom + mResizeBarOverlap;
+ resize_bar_rect.mBottom = panel_rect.mBottom - panel_spacing_round - mResizeBarOverlap;
+ }
+
+ if (mShowDragHandle)
{
- total_height += mPanelSpacing;
+ resize_bar_rect.mLeft += mDragHandleFirstIndent;
+ resize_bar_rect.mRight -= mDragHandleSecondIndent;
}
}
+
+ panelp->setIgnoreReshape(true);
+ panelp->setShape(panel_rect);
+ panelp->setIgnoreReshape(false);
+ panelp->mResizeBar->setShape(resize_bar_rect);
}
- S32 num_resizable_panels = 0;
- S32 shrink_headroom_available = 0;
- S32 shrink_headroom_total = 0;
- for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it)
+ updateResizeBarLimits();
+
+ // clear animation flag at end, since panel resizes will set it
+ // and leave it set if there is any animation in progress
+ mNeedsLayout = continue_animating;
+} // end LLLayoutStack::updateLayout
+
+LLLayoutPanel* LLLayoutStack::findEmbeddedPanel(LLPanel* panelp) const
+{
+ if (!panelp) return NULL;
+
+ e_panel_list_t::const_iterator panel_it;
+ BOOST_FOREACH(LLLayoutPanel* p, mPanels)
{
- // panels that are not fully visible do not count towards shrink headroom
- if ((*panel_it)->getCollapseFactor(mOrientation) < 1.f)
+ if (p == panelp)
{
- continue;
+ return p;
}
+ }
+ return NULL;
+}
- // if currently resizing a panel or the panel is flagged as not automatically resizing
- // only track total available headroom, but don't use it for automatic resize logic
- if ((*panel_it)->mResizeBar->hasMouseCapture()
- || (!(*panel_it)->mAutoResize
- && !force_resize))
+LLLayoutPanel* LLLayoutStack::findEmbeddedPanelByName(const std::string& name) const
+{
+ LLLayoutPanel* result = NULL;
+
+ BOOST_FOREACH(LLLayoutPanel* p, mPanels)
+ {
+ if (p->getName() == name)
{
- if (mOrientation == HORIZONTAL)
- {
- shrink_headroom_total += (*panel_it)->getRect().getWidth() - (*panel_it)->mMinDim;
- }
- else //VERTICAL
- {
- shrink_headroom_total += (*panel_it)->getRect().getHeight() - (*panel_it)->mMinDim;
- }
+ result = p;
+ break;
}
- else
+ }
+
+ return result;
+}
+
+void LLLayoutStack::createResizeBar(LLLayoutPanel* panelp)
+{
+ BOOST_FOREACH(LLLayoutPanel* lp, mPanels)
+ {
+ if (lp->mResizeBar == NULL)
{
- num_resizable_panels++;
- if (mOrientation == HORIZONTAL)
+ LLResizeBar::Params resize_params;
+ resize_params.name("resize");
+ resize_params.resizing_view(lp);
+ resize_params.min_size(lp->getRelevantMinDim());
+ resize_params.side((mOrientation == HORIZONTAL) ? LLResizeBar::RIGHT : LLResizeBar::BOTTOM);
+ resize_params.snapping_enabled(false);
+ LLResizeBar* resize_bar = LLUICtrlFactory::create<LLResizeBar>(resize_params);
+ lp->mResizeBar = resize_bar;
+
+ if (mShowDragHandle)
{
- shrink_headroom_available += (*panel_it)->getRect().getWidth() - (*panel_it)->mMinDim;
- shrink_headroom_total += (*panel_it)->getRect().getWidth() - (*panel_it)->mMinDim;
+ LLPanel::Params resize_bar_bg_panel_p;
+ resize_bar_bg_panel_p.name = "resize_handle_bg_panel";
+ resize_bar_bg_panel_p.rect = lp->mResizeBar->getLocalRect();
+ resize_bar_bg_panel_p.follows.flags = FOLLOWS_ALL;
+ resize_bar_bg_panel_p.tab_stop = false;
+ resize_bar_bg_panel_p.background_visible = true;
+ resize_bar_bg_panel_p.bg_alpha_color = LLUIColorTable::instance().getColor("ResizebarBody");
+ resize_bar_bg_panel_p.has_border = true;
+ resize_bar_bg_panel_p.border.border_thickness = 1;
+ resize_bar_bg_panel_p.border.highlight_light_color = LLUIColorTable::instance().getColor("ResizebarBorderLight");
+ resize_bar_bg_panel_p.border.shadow_dark_color = LLUIColorTable::instance().getColor("ResizebarBorderDark");
+
+ LLPanel* resize_bar_bg_panel = LLUICtrlFactory::create<LLPanel>(resize_bar_bg_panel_p);
+
+ LLIconCtrl::Params icon_p;
+ icon_p.name = "resize_handle_image";
+ icon_p.rect = lp->mResizeBar->getLocalRect();
+ icon_p.follows.flags = FOLLOWS_ALL;
+ icon_p.image = LLUI::getUIImage(mOrientation == HORIZONTAL ? "Vertical Drag Handle" : "Horizontal Drag Handle");
+ resize_bar_bg_panel->addChild(LLUICtrlFactory::create<LLIconCtrl>(icon_p));
+
+ lp->mResizeBar->addChild(resize_bar_bg_panel);
}
- else //VERTICAL
+
+ /*if (mShowDragHandle)
{
- shrink_headroom_available += (*panel_it)->getRect().getHeight() - (*panel_it)->mMinDim;
- shrink_headroom_total += (*panel_it)->getRect().getHeight() - (*panel_it)->mMinDim;
- }
+ LLViewBorder::Params border_params;
+ border_params.border_thickness = 1;
+ border_params.highlight_light_color = LLUIColorTable::instance().getColor("ResizebarBorderLight");
+ border_params.shadow_dark_color = LLUIColorTable::instance().getColor("ResizebarBorderDark");
+
+ addBorder(border_params);
+ setBorderVisible(TRUE);
+
+ LLImagePanel::Params image_panel;
+ mDragHandleImage = LLUI::getUIImage(LLResizeBar::RIGHT == mSide ? "Vertical Drag Handle" : "Horizontal Drag Handle");
+ image_panel.bg_alpha_image = mDragHandleImage;
+ image_panel.background_visible = true;
+ image_panel.horizontal = (LLResizeBar::BOTTOM == mSide);
+ mImagePanel = LLUICtrlFactory::create<LLImagePanel>(image_panel);
+ setImagePanel(mImagePanel);
+ }*/
+
+ //if (mShowDragHandle)
+ //{
+ // setBackgroundVisible(TRUE);
+ // setTransparentColor(LLUIColorTable::instance().getColor("ResizebarBody"));
+ //}
+
+ /*if (mShowDragHandle)
+ {
+ S32 image_width = mDragHandleImage->getTextureWidth();
+ S32 image_height = mDragHandleImage->getTextureHeight();
+ const LLRect& panel_rect = getRect();
+ S32 image_left = (panel_rect.getWidth() - image_width) / 2 - 1;
+ S32 image_bottom = (panel_rect.getHeight() - image_height) / 2;
+ mImagePanel->setRect(LLRect(image_left, image_bottom + image_height, image_left + image_width, image_bottom));
+ }*/
+ LLView::addChild(resize_bar, 0);
}
}
-
- // calculate how many pixels need to be distributed among layout panels
- // positive means panels need to grow, negative means shrink
- S32 pixels_to_distribute;
- if (mOrientation == HORIZONTAL)
+ // bring all resize bars to the front so that they are clickable even over the panels
+ // with a bit of overlap
+ for (e_panel_list_t::iterator panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it)
{
- pixels_to_distribute = getRect().getWidth() - total_width;
+ LLResizeBar* resize_barp = (*panel_it)->mResizeBar;
+ sendChildToFront(resize_barp);
}
- else //VERTICAL
+}
+
+// update layout stack animations, etc. once per frame
+// NOTE: we use this to size world view based on animating UI, *before* we draw the UI
+// we might still need to call updateLayout during UI draw phase, in case UI elements
+// are resizing themselves dynamically
+//static
+void LLLayoutStack::updateClass()
+{
+ for (instance_iter it = beginInstances(); it != endInstances(); ++it)
{
- pixels_to_distribute = getRect().getHeight() - total_height;
+ it->updateLayout();
+ it->mAnimatedThisFrame = false;
}
+}
- // now we distribute the pixels...
- S32 cur_x = 0;
- S32 cur_y = getRect().getHeight();
+void LLLayoutStack::updateFractionalSizes()
+{
+ F32 total_resizable_dim = 0.f;
+
+ BOOST_FOREACH(LLLayoutPanel* panelp, mPanels)
+ {
+ if (panelp->mAutoResize)
+ {
+ total_resizable_dim += llmax(MIN_FRACTIONAL_SIZE, (F32)(panelp->getLayoutDim() - panelp->getRelevantMinDim()));
+ }
+ }
- for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it)
+ BOOST_FOREACH(LLLayoutPanel* panelp, mPanels)
{
- LLPanel* panelp = (*panel_it);
+ if (panelp->mAutoResize)
+ {
+ F32 panel_resizable_dim = llmax(MIN_FRACTIONAL_SIZE, (F32)(panelp->getLayoutDim() - panelp->getRelevantMinDim()));
+ panelp->mFractionalSize = panel_resizable_dim > 0.f
+ ? llclamp(panel_resizable_dim / total_resizable_dim, MIN_FRACTIONAL_SIZE, MAX_FRACTIONAL_SIZE)
+ : MIN_FRACTIONAL_SIZE;
+ llassert(!llisnan(panelp->mFractionalSize));
+ }
+ }
- S32 cur_width = panelp->getRect().getWidth();
- S32 cur_height = panelp->getRect().getHeight();
- S32 new_width = cur_width;
- S32 new_height = cur_height;
+ normalizeFractionalSizes();
+}
- if (mOrientation == HORIZONTAL)
+
+void LLLayoutStack::normalizeFractionalSizes()
+{
+ S32 num_auto_resize_panels = 0;
+ F32 total_fractional_size = 0.f;
+
+ BOOST_FOREACH(LLLayoutPanel* panelp, mPanels)
+ {
+ if (panelp->mAutoResize)
{
- new_width = llmax((*panel_it)->mMinDim, new_width);
+ total_fractional_size += panelp->mFractionalSize;
+ num_auto_resize_panels++;
}
- else
+ }
+
+ if (total_fractional_size == 0.f)
+ { // equal distribution
+ BOOST_FOREACH(LLLayoutPanel* panelp, mPanels)
+ {
+ if (panelp->mAutoResize)
+ {
+ panelp->mFractionalSize = MAX_FRACTIONAL_SIZE / (F32)num_auto_resize_panels;
+ }
+ }
+ }
+ else
+ { // renormalize
+ BOOST_FOREACH(LLLayoutPanel* panelp, mPanels)
{
- new_height = llmax((*panel_it)->mMinDim, new_height);
+ if (panelp->mAutoResize)
+ {
+ panelp->mFractionalSize /= total_fractional_size;
+ }
}
- S32 delta_size = 0;
+ }
+}
- // if panel can automatically resize (not animating, and resize flag set)...
- if ((*panel_it)->getCollapseFactor(mOrientation) == 1.f
- && (force_resize || (*panel_it)->mAutoResize)
- && !(*panel_it)->mResizeBar->hasMouseCapture())
+bool LLLayoutStack::animatePanels()
+{
+ bool continue_animating = false;
+
+ //
+ // animate visibility
+ //
+ BOOST_FOREACH(LLLayoutPanel* panelp, mPanels)
+ {
+ if (panelp->getVisible())
{
- if (mOrientation == HORIZONTAL)
+ if (mAnimate && panelp->mVisibleAmt < 1.f)
{
- // if we're shrinking
- if (pixels_to_distribute < 0)
- {
- // shrink proportionally to amount over minimum
- // so we can do this in one pass
- delta_size = (shrink_headroom_available > 0) ? llround((F32)pixels_to_distribute * ((F32)(cur_width - (*panel_it)->mMinDim) / (F32)shrink_headroom_available)) : 0;
- shrink_headroom_available -= (cur_width - (*panel_it)->mMinDim);
- }
- else
+ if (!mAnimatedThisFrame)
{
- // grow all elements equally
- delta_size = llround((F32)pixels_to_distribute / (F32)num_resizable_panels);
- num_resizable_panels--;
+ panelp->mVisibleAmt = lerp(panelp->mVisibleAmt, 1.f, LLSmoothInterpolation::getInterpolant(mOpenTimeConstant));
+ if (panelp->mVisibleAmt > 0.99f)
+ {
+ panelp->mVisibleAmt = 1.f;
+ }
}
- pixels_to_distribute -= delta_size;
- new_width = llmax((*panel_it)->mMinDim, cur_width + delta_size);
+
+ mAnimatedThisFrame = true;
+ continue_animating = true;
}
else
{
- new_width = getDefaultWidth(new_width);
- }
-
- if (mOrientation == VERTICAL)
- {
- if (pixels_to_distribute < 0)
+ if (panelp->mVisibleAmt != 1.f)
{
- // shrink proportionally to amount over minimum
- // so we can do this in one pass
- delta_size = (shrink_headroom_available > 0) ? llround((F32)pixels_to_distribute * ((F32)(cur_height - (*panel_it)->mMinDim) / (F32)shrink_headroom_available)) : 0;
- shrink_headroom_available -= (cur_height - (*panel_it)->mMinDim);
+ panelp->mVisibleAmt = 1.f;
+ mAnimatedThisFrame = true;
}
- else
+ }
+ }
+ else // not visible
+ {
+ if (mAnimate && panelp->mVisibleAmt > 0.f)
+ {
+ if (!mAnimatedThisFrame)
{
- delta_size = llround((F32)pixels_to_distribute / (F32)num_resizable_panels);
- num_resizable_panels--;
+ panelp->mVisibleAmt = lerp(panelp->mVisibleAmt, 0.f, LLSmoothInterpolation::getInterpolant(mCloseTimeConstant));
+ if (panelp->mVisibleAmt < 0.001f)
+ {
+ panelp->mVisibleAmt = 0.f;
+ }
}
- pixels_to_distribute -= delta_size;
- new_height = llmax((*panel_it)->mMinDim, cur_height + delta_size);
+
+ continue_animating = true;
+ mAnimatedThisFrame = true;
}
else
{
- new_height = getDefaultHeight(new_height);
+ if (panelp->mVisibleAmt != 0.f)
+ {
+ panelp->mVisibleAmt = 0.f;
+ mAnimatedThisFrame = true;
+ }
}
}
- else
+
+ F32 collapse_state = panelp->mCollapsed ? 1.f : 0.f;
+ if (panelp->mCollapseAmt != collapse_state)
{
- if (mOrientation == HORIZONTAL)
+ if (mAnimate)
{
- new_height = getDefaultHeight(new_height);
+ if (!mAnimatedThisFrame)
+ {
+ panelp->mCollapseAmt = lerp(panelp->mCollapseAmt, collapse_state, LLSmoothInterpolation::getInterpolant(mCloseTimeConstant));
+ }
+
+ if (llabs(panelp->mCollapseAmt - collapse_state) < 0.001f)
+ {
+ panelp->mCollapseAmt = collapse_state;
+ }
+
+ mAnimatedThisFrame = true;
+ continue_animating = true;
}
- else // VERTICAL
+ else
{
- new_width = getDefaultWidth(new_width);
+ panelp->mCollapseAmt = collapse_state;
+ mAnimatedThisFrame = true;
}
}
+ }
- // adjust running headroom count based on new sizes
- shrink_headroom_total += delta_size;
+ if (mAnimatedThisFrame) mNeedsLayout = true;
+ return continue_animating;
+}
- LLRect panel_rect;
- panel_rect.setLeftTopAndSize(cur_x, cur_y, new_width, new_height);
- panelp->setShape(panel_rect);
+void LLLayoutStack::updatePanelRect( LLLayoutPanel* resized_panel, const LLRect& new_rect )
+{
+ S32 new_dim = (mOrientation == HORIZONTAL)
+ ? new_rect.getWidth()
+ : new_rect.getHeight();
+ S32 delta_panel_dim = new_dim - resized_panel->getVisibleDim();
+ if (delta_panel_dim == 0) return;
- LLRect resize_bar_rect = panel_rect;
- if (mOrientation == HORIZONTAL)
- {
- resize_bar_rect.mLeft = panel_rect.mRight - resize_bar_overlap;
- resize_bar_rect.mRight = panel_rect.mRight + mPanelSpacing + resize_bar_overlap;
- }
- else
- {
- resize_bar_rect.mTop = panel_rect.mBottom + resize_bar_overlap;
- resize_bar_rect.mBottom = panel_rect.mBottom - mPanelSpacing - resize_bar_overlap;
- }
- (*panel_it)->mResizeBar->setRect(resize_bar_rect);
+ F32 total_visible_fraction = 0.f;
+ F32 delta_auto_resize_headroom = 0.f;
+ F32 old_auto_resize_headroom = 0.f;
- if (mOrientation == HORIZONTAL)
- {
- cur_x += llround(new_width * (*panel_it)->getCollapseFactor(mOrientation)) + mPanelSpacing;
- }
- else //VERTICAL
- {
- cur_y -= llround(new_height * (*panel_it)->getCollapseFactor(mOrientation)) + mPanelSpacing;
- }
- }
+ LLLayoutPanel* other_resize_panel = NULL;
+ LLLayoutPanel* following_panel = NULL;
- // update resize bars with new limits
- LLResizeBar* last_resize_bar = NULL;
- for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it)
+ BOOST_REVERSE_FOREACH(LLLayoutPanel* panelp, mPanels)
{
- LLPanel* panelp = (*panel_it);
-
- if (mOrientation == HORIZONTAL)
+ if (panelp->mAutoResize)
{
- (*panel_it)->mResizeBar->setResizeLimits(
- (*panel_it)->mMinDim,
- (*panel_it)->mMinDim + shrink_headroom_total);
+ old_auto_resize_headroom += (F32)(panelp->mTargetDim - panelp->getRelevantMinDim());
+ if (panelp->getVisible() && !panelp->mCollapsed)
+ {
+ total_visible_fraction += panelp->mFractionalSize;
+ }
}
- else //VERTICAL
+
+ if (panelp == resized_panel)
{
- (*panel_it)->mResizeBar->setResizeLimits(
- (*panel_it)->mMinDim,
- (*panel_it)->mMinDim + shrink_headroom_total);
+ other_resize_panel = following_panel;
}
- // toggle resize bars based on panel visibility, resizability, etc
- BOOL resize_bar_enabled = panelp->getVisible() && (*panel_it)->mUserResize;
- (*panel_it)->mResizeBar->setVisible(resize_bar_enabled);
-
- if (resize_bar_enabled)
+ if (panelp->getVisible() && !panelp->mCollapsed)
{
- last_resize_bar = (*panel_it)->mResizeBar;
+ following_panel = panelp;
}
}
- // hide last resize bar as there is nothing past it
- // resize bars need to be in between two resizable panels
- if (last_resize_bar)
- {
- last_resize_bar->setVisible(FALSE);
- }
-
- // not enough room to fit existing contents
- if (force_resize == FALSE
- // layout did not complete by reaching target position
- && ((mOrientation == VERTICAL && cur_y != -mPanelSpacing)
- || (mOrientation == HORIZONTAL && cur_x != getRect().getWidth() + mPanelSpacing)))
- {
- // do another layout pass with all stacked elements contributing
- // even those that don't usually resize
- llassert_always(force_resize == FALSE);
- updateLayout(TRUE);
- }
-
- mAnimatedThisFrame = true;
-} // end LLLayoutStack::updateLayout
-
-
-LLLayoutPanel* LLLayoutStack::findEmbeddedPanel(LLPanel* panelp) const
-{
- if (!panelp) return NULL;
-
- e_panel_list_t::const_iterator panel_it;
- for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it)
+ if (resized_panel->mAutoResize)
{
- if ((*panel_it) == panelp)
+ if (!other_resize_panel || !other_resize_panel->mAutoResize)
{
- return *panel_it;
+ delta_auto_resize_headroom += delta_panel_dim;
}
}
- return NULL;
-}
-
-LLLayoutPanel* LLLayoutStack::findEmbeddedPanelByName(const std::string& name) const
-{
- LLLayoutPanel* result = NULL;
-
- for (e_panel_list_t::const_iterator panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it)
+ else
{
- LLLayoutPanel* p = *panel_it;
-
- if (p->getName() == name)
+ if (!other_resize_panel || other_resize_panel->mAutoResize)
{
- result = p;
- break;
+ delta_auto_resize_headroom -= delta_panel_dim;
}
}
- return result;
-}
+ F32 fraction_given_up = 0.f;
+ F32 fraction_remaining = 1.f;
+ F32 new_auto_resize_headroom = old_auto_resize_headroom + delta_auto_resize_headroom;
-// Compute sum of min_width or min_height of children
-void LLLayoutStack::calcMinExtents()
-{
- mMinWidth = 0;
- mMinHeight = 0;
+ enum
+ {
+ BEFORE_RESIZED_PANEL,
+ RESIZED_PANEL,
+ NEXT_PANEL,
+ AFTER_RESIZED_PANEL
+ } which_panel = BEFORE_RESIZED_PANEL;
- e_panel_list_t::iterator panel_it;
- for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it)
+ BOOST_FOREACH(LLLayoutPanel* panelp, mPanels)
{
- if (mOrientation == HORIZONTAL)
+ if (!panelp->getVisible() || panelp->mCollapsed)
{
- mMinWidth += (*panel_it)->mMinDim;
- if (panel_it != mPanels.begin())
+ if (panelp->mAutoResize)
{
- mMinWidth += mPanelSpacing;
+ fraction_remaining -= panelp->mFractionalSize;
}
+ continue;
}
- else //VERTICAL
+
+ if (panelp == resized_panel)
{
- mMinHeight += (*panel_it)->mMinDim;
- if (panel_it != mPanels.begin())
- {
- mMinHeight += mPanelSpacing;
- }
+ which_panel = RESIZED_PANEL;
}
- }
-}
-void LLLayoutStack::createResizeBars()
-{
- for (e_panel_list_t::iterator panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it)
- {
- LLLayoutPanel* lp = (*panel_it);
- if (lp->mResizeBar == NULL)
+ switch(which_panel)
{
- LLResizeBar::Side side = (mOrientation == HORIZONTAL) ? LLResizeBar::RIGHT : LLResizeBar::BOTTOM;
- LLRect resize_bar_rect = getRect();
-
- LLResizeBar::Params resize_params;
- resize_params.name("resize");
- resize_params.resizing_view(lp);
- resize_params.min_size(lp->mMinDim);
- resize_params.side(side);
- resize_params.snapping_enabled(false);
- LLResizeBar* resize_bar = LLUICtrlFactory::create<LLResizeBar>(resize_params);
- lp->mResizeBar = resize_bar;
- LLView::addChild(resize_bar, 0);
+ case BEFORE_RESIZED_PANEL:
+ if (panelp->mAutoResize)
+ { // freeze current size as fraction of overall auto_resize space
+ F32 fractional_adjustment_factor = new_auto_resize_headroom == 0.f
+ ? 1.f
+ : old_auto_resize_headroom / new_auto_resize_headroom;
+ F32 new_fractional_size = llclamp(panelp->mFractionalSize * fractional_adjustment_factor,
+ MIN_FRACTIONAL_SIZE,
+ MAX_FRACTIONAL_SIZE);
+ fraction_given_up -= new_fractional_size - panelp->mFractionalSize;
+ fraction_remaining -= panelp->mFractionalSize;
+ panelp->mFractionalSize = new_fractional_size;
+ llassert(!llisnan(panelp->mFractionalSize));
+ }
+ else
+ {
+ // leave non auto-resize panels alone
+ }
+ break;
+ case RESIZED_PANEL:
+ if (panelp->mAutoResize)
+ { // freeze new size as fraction
+ F32 new_fractional_size = (new_auto_resize_headroom == 0.f)
+ ? MAX_FRACTIONAL_SIZE
+ : llclamp(total_visible_fraction * (F32)(new_dim - panelp->getRelevantMinDim()) / new_auto_resize_headroom, MIN_FRACTIONAL_SIZE, MAX_FRACTIONAL_SIZE);
+ fraction_given_up -= new_fractional_size - panelp->mFractionalSize;
+ fraction_remaining -= panelp->mFractionalSize;
+ panelp->mFractionalSize = new_fractional_size;
+ llassert(!llisnan(panelp->mFractionalSize));
+ }
+ else
+ { // freeze new size as original size
+ panelp->mTargetDim = new_dim;
+ }
+ which_panel = NEXT_PANEL;
+ break;
+ case NEXT_PANEL:
+ if (panelp->mAutoResize)
+ {
+ fraction_remaining -= panelp->mFractionalSize;
+ if (resized_panel->mAutoResize)
+ {
+ panelp->mFractionalSize = llclamp(panelp->mFractionalSize + fraction_given_up, MIN_FRACTIONAL_SIZE, MAX_FRACTIONAL_SIZE);
+ fraction_given_up = 0.f;
+ }
+ else
+ {
+ if (new_auto_resize_headroom < 1.f)
+ {
+ new_auto_resize_headroom = 1.f;
+ }
- // bring all resize bars to the front so that they are clickable even over the panels
- // with a bit of overlap
- for (e_panel_list_t::iterator panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it)
+ F32 new_fractional_size = llclamp(total_visible_fraction * (F32)(panelp->mTargetDim - panelp->getRelevantMinDim() + delta_auto_resize_headroom)
+ / new_auto_resize_headroom,
+ MIN_FRACTIONAL_SIZE,
+ MAX_FRACTIONAL_SIZE);
+ fraction_given_up -= new_fractional_size - panelp->mFractionalSize;
+ panelp->mFractionalSize = new_fractional_size;
+ }
+ }
+ else
+ {
+ panelp->mTargetDim -= delta_panel_dim;
+ }
+ which_panel = AFTER_RESIZED_PANEL;
+ break;
+ case AFTER_RESIZED_PANEL:
+ if (panelp->mAutoResize && fraction_given_up != 0.f)
{
- LLResizeBar* resize_barp = (*panel_it)->mResizeBar;
- sendChildToFront(resize_barp);
+ panelp->mFractionalSize = llclamp(panelp->mFractionalSize + (panelp->mFractionalSize / fraction_remaining) * fraction_given_up,
+ MIN_FRACTIONAL_SIZE,
+ MAX_FRACTIONAL_SIZE);
}
+ default:
+ break;
}
}
+ updateLayout();
+ //normalizeFractionalSizes();
}
-// 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()
+void LLLayoutStack::reshape(S32 width, S32 height, BOOL called_from_parent)
+{
+ mNeedsLayout = true;
+ LLView::reshape(width, height, called_from_parent);
+}
+
+void LLLayoutStack::updateResizeBarLimits()
{
- LLInstanceTrackerScopedGuard guard;
- for (LLLayoutStack::instance_iter it = guard.beginInstances();
- it != guard.endInstances();
- ++it)
+ LLLayoutPanel* previous_visible_panelp = NULL;
+ BOOST_REVERSE_FOREACH(LLLayoutPanel* visible_panelp, mPanels)
{
- it->updateLayout();
+ if (!visible_panelp->getVisible() || visible_panelp->mCollapsed)
+ {
+ visible_panelp->mResizeBar->setVisible(FALSE);
+ continue;
+ }
+
+ // toggle resize bars based on panel visibility, resizability, etc
+ if (previous_visible_panelp
+ && (visible_panelp->mUserResize || previous_visible_panelp->mUserResize) // one of the pair is user resizable
+ && (visible_panelp->mAutoResize || visible_panelp->mUserResize) // current panel is resizable
+ && (previous_visible_panelp->mAutoResize || previous_visible_panelp->mUserResize)) // previous panel is resizable
+ {
+ visible_panelp->mResizeBar->setVisible(TRUE);
+ S32 previous_panel_headroom = previous_visible_panelp->getVisibleDim() - previous_visible_panelp->getRelevantMinDim();
+ visible_panelp->mResizeBar->setResizeLimits(visible_panelp->getRelevantMinDim(),
+ visible_panelp->getVisibleDim() + previous_panel_headroom);
+ }
+ else
+ {
+ visible_panelp->mResizeBar->setVisible(FALSE);
+ }
+
+ previous_visible_panelp = visible_panelp;
}
}
+
diff --git a/indra/llui/lllayoutstack.h b/indra/llui/lllayoutstack.h
index 4ac8ef0ee9..a245ebe1b9 100644..100755
--- a/indra/llui/lllayoutstack.h
+++ b/indra/llui/lllayoutstack.h
@@ -5,7 +5,7 @@
*
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
+ * Copyright (C) 2010, Linden Reshasearch, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -29,37 +29,33 @@
#define LL_LLLAYOUTSTACK_H
#include "llpanel.h"
+#include "llresizebar.h"
-class LLPanel;
class LLLayoutPanel;
+
class LLLayoutStack : public LLView, public LLInstanceTracker<LLLayoutStack>
{
public:
- typedef enum e_layout_orientation
- {
- HORIZONTAL,
- VERTICAL
- } ELayoutOrientation;
-
- struct OrientationNames
- : public LLInitParam::TypeValuesHelper<ELayoutOrientation, OrientationNames>
- {
- static void declareValues();
- };
struct LayoutStackRegistry : public LLChildRegistry<LayoutStackRegistry>
{};
struct Params : public LLInitParam::Block<Params, LLView::Params>
{
- Mandatory<ELayoutOrientation, OrientationNames> orientation;
+ Mandatory<EOrientation> orientation;
Optional<S32> border_size;
Optional<bool> animate,
clip;
Optional<F32> open_time_constant,
close_time_constant;
+ Optional<S32> resize_bar_overlap;
+ Optional<bool> show_drag_handle;
+ Optional<S32> drag_handle_first_indent;
+ Optional<S32> drag_handle_second_indent;
+ Optional<S32> drag_handle_thickness;
+ Optional<S32> drag_handle_shift;
Params();
};
@@ -72,12 +68,11 @@ public:
/*virtual*/ void removeChild(LLView*);
/*virtual*/ BOOL postBuild();
/*virtual*/ bool addChild(LLView* child, S32 tab_group = 0);
+ /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
+
static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLXMLNodePtr output_node = NULL);
- S32 getMinWidth() const { return mMinWidth; }
- S32 getMinHeight() const { return mMinHeight; }
-
typedef enum e_animate
{
NO_ANIMATE,
@@ -85,70 +80,54 @@ public:
} EAnimate;
void addPanel(LLLayoutPanel* panel, EAnimate animate = NO_ANIMATE);
- 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 dimension along layout_stack axis of the specified by name panel.
- *
- * @returns true if specified by panel_name internal panel exists, false otherwise.
- */
- bool getPanelMinSize(const std::string& panel_name, S32* min_dimp);
-
- /**
- * Gets maximal dimension along layout_stack axis of the specified by name panel.
- *
- * @returns true if specified by panel_name internal panel exists, false otherwise.
- */
- bool getPanelMaxSize(const std::string& panel_name, S32* max_dim);
-
- void updateLayout(BOOL force_resize = FALSE);
-
+
+ void updateLayout();
+
S32 getPanelSpacing() const { return mPanelSpacing; }
- BOOL getAnimate () const { return mAnimate; }
- void setAnimate (BOOL animate) { mAnimate = animate; }
static void updateClass();
protected:
LLLayoutStack(const Params&);
friend class LLUICtrlFactory;
+ friend class LLLayoutPanel;
private:
- void createResizeBars();
- void calcMinExtents();
- S32 getDefaultHeight(S32 cur_height);
- S32 getDefaultWidth(S32 cur_width);
+ void updateResizeBarLimits();
+ bool animatePanels();
+ void createResizeBar(LLLayoutPanel* panel);
- const ELayoutOrientation mOrientation;
+ const EOrientation mOrientation;
typedef std::vector<LLLayoutPanel*> e_panel_list_t;
e_panel_list_t mPanels;
LLLayoutPanel* findEmbeddedPanel(LLPanel* panelp) const;
LLLayoutPanel* findEmbeddedPanelByName(const std::string& name) const;
+ void updateFractionalSizes();
+ void normalizeFractionalSizes();
+ void updatePanelRect( LLLayoutPanel* param1, const LLRect& new_rect );
- S32 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;
- F32 mOpenTimeConstant;
- F32 mCloseTimeConstant;
+ F32 mOpenTimeConstant;
+ F32 mCloseTimeConstant;
+ bool mNeedsLayout;
+ S32 mResizeBarOverlap;
+ bool mShowDragHandle;
+ S32 mDragHandleFirstIndent;
+ S32 mDragHandleSecondIndent;
+ S32 mDragHandleThickness;
+ S32 mDragHandleShift;
}; // end class LLLayoutStack
+
class LLLayoutPanel : public LLPanel
{
friend class LLLayoutStack;
@@ -156,44 +135,74 @@ friend class LLUICtrlFactory;
public:
struct Params : public LLInitParam::Block<Params, LLPanel::Params>
{
- Optional<S32> min_dim,
- max_dim;
+ Optional<S32> expanded_min_dim,
+ min_dim;
Optional<bool> user_resize,
auto_resize;
- Params()
- : min_dim("min_dim", 0),
- max_dim("max_dim", 0),
- user_resize("user_resize", true),
- auto_resize("auto_resize", true)
- {
- addSynonym(min_dim, "min_width");
- addSynonym(min_dim, "min_height");
- addSynonym(max_dim, "max_width");
- addSynonym(max_dim, "max_height");
- }
+ Params();
};
~LLLayoutPanel();
void initFromParams(const Params& p);
- void setMinDim(S32 value) { mMinDim = value; }
- void setMaxDim(S32 value) { mMaxDim = value; }
-protected:
- LLLayoutPanel(const Params& p) ;
+ void handleReshape(const LLRect& new_rect, bool by_user);
+
+ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
+
+ void setVisible(BOOL visible);
+
+ S32 getLayoutDim() const;
+ S32 getTargetDim() const;
+ void setTargetDim(S32 value);
+ S32 getMinDim() const { return llmax(0, mMinDim); }
+ void setMinDim(S32 value) { mMinDim = value; }
+
+ S32 getExpandedMinDim() const { return mExpandedMinDim >= 0 ? mExpandedMinDim : getMinDim(); }
+ void setExpandedMinDim(S32 value) { mExpandedMinDim = value; }
- F32 getCollapseFactor(LLLayoutStack::ELayoutOrientation orientation);
+ S32 getRelevantMinDim() const
+ {
+ S32 min_dim = mMinDim;
+
+ if (!mCollapsed)
+ {
+ min_dim = getExpandedMinDim();
+ }
+
+ return min_dim;
+ }
+
+ F32 getAutoResizeFactor() const;
+ F32 getVisibleAmount() const;
+ S32 getVisibleDim() const;
+ LLResizeBar* getResizeBar() { return mResizeBar; }
+
+ bool isCollapsed() const { return mCollapsed;}
- S32 mMinDim;
- S32 mMaxDim;
- BOOL mAutoResize;
- BOOL mUserResize;
- BOOL mCollapsed;
+ void setOrientation(LLView::EOrientation orientation);
+ void storeOriginalDim();
+
+ void setIgnoreReshape(bool ignore) { mIgnoreReshape = ignore; }
+
+protected:
+ LLLayoutPanel(const Params& p);
+
+ const bool mAutoResize;
+ const bool mUserResize;
+
+ S32 mExpandedMinDim;
+ S32 mMinDim;
+ bool mCollapsed;
+ F32 mVisibleAmt;
+ F32 mCollapseAmt;
+ F32 mFractionalSize;
+ S32 mTargetDim;
+ bool mIgnoreReshape;
+ LLView::EOrientation mOrientation;
class LLResizeBar* mResizeBar;
- F32 mVisibleAmt;
- F32 mCollapseAmt;
};
diff --git a/indra/llui/lllazyvalue.h b/indra/llui/lllazyvalue.h
index 0fc95d9efa..0fc95d9efa 100644..100755
--- a/indra/llui/lllazyvalue.h
+++ b/indra/llui/lllazyvalue.h
diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp
index 7e348656a9..a08cf91a69 100644..100755
--- a/indra/llui/lllineeditor.cpp
+++ b/indra/llui/lllineeditor.cpp
@@ -37,6 +37,7 @@
#include "llgl.h"
#include "lltimer.h"
+#include "llcalc.h"
//#include "llclipboard.h"
#include "llcontrol.h"
#include "llbutton.h"
@@ -44,6 +45,7 @@
#include "llkeyboard.h"
#include "llrect.h"
#include "llresmgr.h"
+#include "llspellcheck.h"
#include "llstring.h"
#include "llwindow.h"
#include "llui.h"
@@ -64,6 +66,7 @@ const S32 SCROLL_INCREMENT_ADD = 0; // make space for typing
const S32 SCROLL_INCREMENT_DEL = 4; // make space for baskspacing
const F32 AUTO_SCROLL_TIME = 0.05f;
const F32 TRIPLE_CLICK_INTERVAL = 0.3f; // delay between double and triple click. *TODO: make this equal to the double click interval?
+const F32 SPELLCHECK_DELAY = 0.5f; // delay between the last keypress and spell checking the word the cursor is on
const std::string PASSWORD_ASTERISK( "\xE2\x80\xA2" ); // U+2022 BULLET
@@ -81,11 +84,14 @@ LLLineEditor::Params::Params()
: max_length(""),
keystroke_callback("keystroke_callback"),
prevalidate_callback("prevalidate_callback"),
+ prevalidate_input_callback("prevalidate_input_callback"),
background_image("background_image"),
background_image_disabled("background_image_disabled"),
background_image_focused("background_image_focused"),
+ bg_image_always_focused("bg_image_always_focused", false),
select_on_focus("select_on_focus", false),
revert_on_esc("revert_on_esc", true),
+ spellcheck("spellcheck", false),
commit_on_focus_lost("commit_on_focus_lost", true),
ignore_tab("ignore_tab", true),
is_password("is_password", false),
@@ -101,10 +107,11 @@ LLLineEditor::Params::Params()
text_pad_right("text_pad_right"),
default_text("default_text")
{
- mouse_opaque = true;
+ changeDefault(mouse_opaque, true);
addSynonym(select_on_focus, "select_all_on_focus_received");
addSynonym(border, "border");
addSynonym(label, "watermark_text");
+ addSynonym(max_length.chars, "max_length");
}
LLLineEditor::LLLineEditor(const LLLineEditor::Params& p)
@@ -131,12 +138,17 @@ LLLineEditor::LLLineEditor(const LLLineEditor::Params& p)
mIgnoreArrowKeys( FALSE ),
mIgnoreTab( p.ignore_tab ),
mDrawAsterixes( p.is_password ),
+ mSpellCheck( p.spellcheck ),
+ mSpellCheckStart(-1),
+ mSpellCheckEnd(-1),
mSelectAllonFocusReceived( p.select_on_focus ),
+ mSelectAllonCommit( TRUE ),
mPassDelete(FALSE),
mReadOnly(FALSE),
mBgImage( p.background_image ),
mBgImageDisabled( p.background_image_disabled ),
mBgImageFocused( p.background_image_focused ),
+ mShowImageFocused( p.bg_image_always_focused ),
mHaveHistory(FALSE),
mReplaceNewlinesWithSpaces( TRUE ),
mLabel(p.label),
@@ -173,8 +185,16 @@ LLLineEditor::LLLineEditor(const LLLineEditor::Params& p)
updateTextPadding();
setCursor(mText.length());
+ if (mSpellCheck)
+ {
+ LLSpellChecker::setSettingsChangeCallback(boost::bind(&LLLineEditor::onSpellCheckSettingsChange, this));
+ }
+ mSpellCheckTimer.reset();
+
+ setPrevalidateInput(p.prevalidate_input_callback());
setPrevalidate(p.prevalidate_callback());
+ llassert(LLMenuGL::sMenuContainer != NULL);
LLContextMenu* menu = LLUICtrlFactory::instance().createFromFile<LLContextMenu>
("menu_text_editor.xml",
LLMenuGL::sMenuContainer,
@@ -185,14 +205,22 @@ LLLineEditor::LLLineEditor(const LLLineEditor::Params& p)
LLLineEditor::~LLLineEditor()
{
mCommitOnFocusLost = FALSE;
+
+ // Make sure no context menu linger around once the widget is deleted
+ LLContextMenu* menu = static_cast<LLContextMenu*>(mContextMenuHandle.get());
+ if (menu)
+ {
+ menu->hide();
+ }
+ setContextMenu(NULL);
// calls onCommit() while LLLineEditor still valid
gFocusMgr.releaseFocusIfNeeded( this );
}
-
void LLLineEditor::onFocusReceived()
{
+ gEditMenuHandler = this;
LLUICtrl::onFocusReceived();
updateAllowingLanguageInput();
}
@@ -228,7 +256,11 @@ void LLLineEditor::onCommit()
setControlValue(getValue());
LLUICtrl::onCommit();
- selectAll();
+ resetDirty();
+
+ // Selection on commit needs to be turned off when evaluating maths
+ // expressions, to allow indication of the error position
+ if (mSelectAllonCommit) selectAll();
}
// Returns TRUE if user changed value at all
@@ -389,7 +421,11 @@ void LLLineEditor::setText(const LLStringExplicit &new_text)
setCursor(llmin((S32)mText.length(), getCursor()));
// Set current history line to end of history.
- if(mLineHistory.end() != mLineHistory.begin())
+ if (mLineHistory.empty())
+ {
+ mCurrentHistoryLine = mLineHistory.end();
+ }
+ else
{
mCurrentHistoryLine = mLineHistory.end() - 1;
}
@@ -401,23 +437,15 @@ void LLLineEditor::setText(const LLStringExplicit &new_text)
// Picks a new cursor position based on the actual screen size of text being drawn.
void LLLineEditor::setCursorAtLocalPos( S32 local_mouse_x )
{
- const llwchar* wtext = mText.getWString().c_str();
- LLWString asterix_text;
- if (mDrawAsterixes)
- {
- for (S32 i = 0; i < mText.length(); i++)
- {
- asterix_text += utf8str_to_wstring(PASSWORD_ASTERISK);
- }
- wtext = asterix_text.c_str();
- }
+ S32 cursor_pos = calcCursorPos(local_mouse_x);
+
+ S32 left_pos = llmin( mSelectionStart, cursor_pos );
+ S32 length = llabs( mSelectionStart - cursor_pos );
+ const LLWString& substr = mText.getWString().substr(left_pos, length);
+
+ if (mIsSelecting && !prevalidateInput(substr))
+ return;
- S32 cursor_pos =
- mScrollHPos +
- mGLFont->charFromPixelOffset(
- wtext, mScrollHPos,
- (F32)(local_mouse_x - mTextLeftEdge),
- (F32)(mTextRightEdge - mTextLeftEdge + 1)); // min-max range is inclusive
setCursor(cursor_pos);
}
@@ -501,6 +529,11 @@ BOOL LLLineEditor::canSelectAll() const
void LLLineEditor::selectAll()
{
+ if (!prevalidateInput(mText.getWString()))
+ {
+ return;
+ }
+
mSelectionStart = mText.length();
mSelectionEnd = 0;
setCursor(mSelectionEnd);
@@ -509,6 +542,99 @@ void LLLineEditor::selectAll()
updatePrimary();
}
+bool LLLineEditor::getSpellCheck() const
+{
+ return (LLSpellChecker::getUseSpellCheck()) && (!mReadOnly) && (mSpellCheck);
+}
+
+const std::string& LLLineEditor::getSuggestion(U32 index) const
+{
+ return (index < mSuggestionList.size()) ? mSuggestionList[index] : LLStringUtil::null;
+}
+
+U32 LLLineEditor::getSuggestionCount() const
+{
+ return mSuggestionList.size();
+}
+
+void LLLineEditor::replaceWithSuggestion(U32 index)
+{
+ for (std::list<std::pair<U32, U32> >::const_iterator it = mMisspellRanges.begin(); it != mMisspellRanges.end(); ++it)
+ {
+ if ( (it->first <= (U32)mCursorPos) && (it->second >= (U32)mCursorPos) )
+ {
+ deselect();
+
+ // Delete the misspelled word
+ mText.erase(it->first, it->second - it->first);
+
+ // Insert the suggestion in its place
+ LLWString suggestion = utf8str_to_wstring(mSuggestionList[index]);
+ mText.insert(it->first, suggestion);
+ setCursor(it->first + (S32)suggestion.length());
+
+ break;
+ }
+ }
+ mSpellCheckStart = mSpellCheckEnd = -1;
+}
+
+void LLLineEditor::addToDictionary()
+{
+ if (canAddToDictionary())
+ {
+ LLSpellChecker::instance().addToCustomDictionary(getMisspelledWord(mCursorPos));
+ }
+}
+
+bool LLLineEditor::canAddToDictionary() const
+{
+ return (getSpellCheck()) && (isMisspelledWord(mCursorPos));
+}
+
+void LLLineEditor::addToIgnore()
+{
+ if (canAddToIgnore())
+ {
+ LLSpellChecker::instance().addToIgnoreList(getMisspelledWord(mCursorPos));
+ }
+}
+
+bool LLLineEditor::canAddToIgnore() const
+{
+ return (getSpellCheck()) && (isMisspelledWord(mCursorPos));
+}
+
+std::string LLLineEditor::getMisspelledWord(U32 pos) const
+{
+ for (std::list<std::pair<U32, U32> >::const_iterator it = mMisspellRanges.begin(); it != mMisspellRanges.end(); ++it)
+ {
+ if ( (it->first <= pos) && (it->second >= pos) )
+ {
+ return wstring_to_utf8str(mText.getWString().substr(it->first, it->second - it->first));
+ }
+ }
+ return LLStringUtil::null;
+}
+
+bool LLLineEditor::isMisspelledWord(U32 pos) const
+{
+ for (std::list<std::pair<U32, U32> >::const_iterator it = mMisspellRanges.begin(); it != mMisspellRanges.end(); ++it)
+ {
+ if ( (it->first <= pos) && (it->second >= pos) )
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+void LLLineEditor::onSpellCheckSettingsChange()
+{
+ // Recheck the spelling on every change
+ mMisspellRanges.clear();
+ mSpellCheckStart = mSpellCheckEnd = -1;
+}
BOOL LLLineEditor::handleDoubleClick(S32 x, S32 y, MASK mask)
{
@@ -586,6 +712,9 @@ BOOL LLLineEditor::handleMouseDown(S32 x, S32 y, MASK mask)
if (mask & MASK_SHIFT)
{
+ // assume we're starting a drag select
+ mIsSelecting = TRUE;
+
// Handle selection extension
S32 old_cursor_pos = getCursor();
setCursorAtLocalPos(x);
@@ -620,8 +749,6 @@ BOOL LLLineEditor::handleMouseDown(S32 x, S32 y, MASK mask)
mSelectionStart = old_cursor_pos;
mSelectionEnd = getCursor();
}
- // assume we're starting a drag select
- mIsSelecting = TRUE;
}
else
{
@@ -662,7 +789,7 @@ BOOL LLLineEditor::handleMouseDown(S32 x, S32 y, MASK mask)
BOOL LLLineEditor::handleMiddleMouseDown(S32 x, S32 y, MASK mask)
{
- // llinfos << "MiddleMouseDown" << llendl;
+ // LL_INFOS() << "MiddleMouseDown" << LL_ENDL;
setFocus( TRUE );
if( canPastePrimary() )
{
@@ -704,7 +831,7 @@ BOOL LLLineEditor::handleHover(S32 x, S32 y, MASK mask)
// Scroll if mouse cursor outside of bounds
if (mScrollTimer.hasExpired())
{
- S32 increment = llround(mScrollTimer.getElapsedTimeF32() / AUTO_SCROLL_TIME);
+ S32 increment = ll_round(mScrollTimer.getElapsedTimeF32() / AUTO_SCROLL_TIME);
mScrollTimer.reset();
mScrollTimer.setTimerExpirySec(AUTO_SCROLL_TIME);
if( (x < mTextLeftEdge) && (mScrollHPos > 0 ) )
@@ -732,14 +859,14 @@ BOOL LLLineEditor::handleHover(S32 x, S32 y, MASK mask)
mKeystrokeTimer.reset();
getWindow()->setCursor(UI_CURSOR_IBEAM);
- lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (active)" << llendl;
+ LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (active)" << LL_ENDL;
handled = TRUE;
}
if( !handled )
{
getWindow()->setCursor(UI_CURSOR_IBEAM);
- lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (inactive)" << llendl;
+ LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (inactive)" << LL_ENDL;
handled = TRUE;
}
@@ -792,6 +919,9 @@ void LLLineEditor::removeChar()
{
if( getCursor() > 0 )
{
+ if (!prevalidateInput(mText.getWString().substr(getCursor()-1, 1)))
+ return;
+
mText.erase(getCursor() - 1, 1);
setCursor(getCursor() - 1);
@@ -812,6 +942,9 @@ void LLLineEditor::addChar(const llwchar uni_char)
}
else if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode())
{
+ if (!prevalidateInput(mText.getWString().substr(getCursor(), 1)))
+ return;
+
mText.erase(getCursor(), 1);
}
@@ -860,6 +993,13 @@ void LLLineEditor::extendSelection( S32 new_cursor_pos )
startSelection();
}
+ S32 left_pos = llmin( mSelectionStart, new_cursor_pos );
+ S32 selection_length = llabs( mSelectionStart - new_cursor_pos );
+ const LLWString& selection = mText.getWString().substr(left_pos, selection_length);
+
+ if (!prevalidateInput(selection))
+ return;
+
setCursor(new_cursor_pos);
mSelectionEnd = getCursor();
}
@@ -990,8 +1130,12 @@ void LLLineEditor::deleteSelection()
{
if( !mReadOnly && hasSelection() )
{
- S32 left_pos = llmin( mSelectionStart, mSelectionEnd );
- S32 selection_length = llabs( mSelectionStart - mSelectionEnd );
+ S32 left_pos, selection_length;
+ getSelectionRange(&left_pos, &selection_length);
+ const LLWString& selection = mText.getWString().substr(left_pos, selection_length);
+
+ if (!prevalidateInput(selection))
+ return;
mText.erase(left_pos, selection_length);
deselect();
@@ -1009,13 +1153,17 @@ void LLLineEditor::cut()
{
if( canCut() )
{
+ S32 left_pos, length;
+ getSelectionRange(&left_pos, &length);
+ const LLWString& selection = mText.getWString().substr(left_pos, length);
+
+ if (!prevalidateInput(selection))
+ return;
+
// Prepare for possible rollback
LLLineEditorRollback rollback( this );
-
- S32 left_pos = llmin( mSelectionStart, mSelectionEnd );
- S32 length = llabs( mSelectionStart - mSelectionEnd );
- gClipboard.copyFromSubstring( mText.getWString(), left_pos, length );
+ LLClipboard::instance().copyToClipboard( mText.getWString(), left_pos, length );
deleteSelection();
// Validate new string and rollback the if needed.
@@ -1026,9 +1174,8 @@ void LLLineEditor::cut()
LLUI::reportBadKeystroke();
}
else
- if( mKeystrokeCallback )
{
- mKeystrokeCallback( this );
+ onKeystroke();
}
}
}
@@ -1046,13 +1193,13 @@ void LLLineEditor::copy()
{
S32 left_pos = llmin( mSelectionStart, mSelectionEnd );
S32 length = llabs( mSelectionStart - mSelectionEnd );
- gClipboard.copyFromSubstring( mText.getWString(), left_pos, length );
+ LLClipboard::instance().copyToClipboard( mText.getWString(), left_pos, length );
}
}
BOOL LLLineEditor::canPaste() const
{
- return !mReadOnly && gClipboard.canPasteString();
+ return !mReadOnly && LLClipboard::instance().isTextAvailable();
}
void LLLineEditor::paste()
@@ -1083,17 +1230,13 @@ void LLLineEditor::pasteHelper(bool is_primary)
if (can_paste_it)
{
LLWString paste;
- if (is_primary)
- {
- paste = gClipboard.getPastePrimaryWString();
- }
- else
- {
- paste = gClipboard.getPasteWString();
- }
+ LLClipboard::instance().pasteFromClipboard(paste, is_primary);
if (!paste.empty())
{
+ if (!prevalidateInput(paste))
+ return;
+
// Prepare for possible rollback
LLLineEditorRollback rollback(this);
@@ -1159,9 +1302,8 @@ void LLLineEditor::pasteHelper(bool is_primary)
LLUI::reportBadKeystroke();
}
else
- if( mKeystrokeCallback )
{
- mKeystrokeCallback( this );
+ onKeystroke();
}
}
}
@@ -1174,13 +1316,13 @@ void LLLineEditor::copyPrimary()
{
S32 left_pos = llmin( mSelectionStart, mSelectionEnd );
S32 length = llabs( mSelectionStart - mSelectionEnd );
- gClipboard.copyFromPrimarySubstring( mText.getWString(), left_pos, length );
+ LLClipboard::instance().copyToClipboard( mText.getWString(), left_pos, length, true);
}
}
BOOL LLLineEditor::canPastePrimary() const
{
- return !mReadOnly && gClipboard.canPastePrimaryString();
+ return !mReadOnly && LLClipboard::instance().isTextAvailable(true);
}
void LLLineEditor::updatePrimary()
@@ -1209,7 +1351,7 @@ BOOL LLLineEditor::handleSpecialKey(KEY key, MASK mask)
case KEY_BACKSPACE:
if (!mReadOnly)
{
- //llinfos << "Handling backspace" << llendl;
+ //LL_INFOS() << "Handling backspace" << LL_ENDL;
if( hasSelection() )
{
deleteSelection();
@@ -1414,9 +1556,10 @@ BOOL LLLineEditor::handleKeyHere(KEY key, MASK mask )
// Notify owner if requested
if (!need_to_rollback && handled)
{
- if (mKeystrokeCallback)
+ onKeystroke();
+ if ( (!selection_modified) && (KEY_BACKSPACE == key) )
{
- mKeystrokeCallback(this);
+ mSpellCheckTimer.setTimerExpirySec(SPELLCHECK_DELAY);
}
}
}
@@ -1441,6 +1584,13 @@ BOOL LLLineEditor::handleUnicodeCharHere(llwchar uni_char)
LLLineEditorRollback rollback( this );
+ {
+ LLWString u_char;
+ u_char.assign(1, uni_char);
+ if (!prevalidateInput(u_char))
+ return handled;
+ }
+
addChar(uni_char);
mKeystrokeTimer.reset();
@@ -1462,12 +1612,11 @@ BOOL LLLineEditor::handleUnicodeCharHere(llwchar uni_char)
// Notify owner if requested
if( !need_to_rollback && handled )
{
- if( mKeystrokeCallback )
- {
- // HACK! The only usage of this callback doesn't do anything with the character.
- // We'll have to do something about this if something ever changes! - Doug
- mKeystrokeCallback( this );
- }
+ // HACK! The only usage of this callback doesn't do anything with the character.
+ // We'll have to do something about this if something ever changes! - Doug
+ onKeystroke();
+
+ mSpellCheckTimer.setTimerExpirySec(SPELLCHECK_DELAY);
}
}
return handled;
@@ -1492,6 +1641,13 @@ void LLLineEditor::doDelete()
}
else if ( getCursor() < mText.length())
{
+ const LLWString& text_to_delete = mText.getWString().substr(getCursor(), 1);
+
+ if (!prevalidateInput(text_to_delete))
+ {
+ onKeystroke();
+ return;
+ }
setCursor(getCursor() + 1);
removeChar();
}
@@ -1505,10 +1661,9 @@ void LLLineEditor::doDelete()
}
else
{
- if( mKeystrokeCallback )
- {
- mKeystrokeCallback( this );
- }
+ onKeystroke();
+
+ mSpellCheckTimer.setTimerExpirySec(SPELLCHECK_DELAY);
}
}
}
@@ -1522,7 +1677,7 @@ void LLLineEditor::drawBackground()
{
image = mBgImageDisabled;
}
- else if ( has_focus )
+ else if ( has_focus || mShowImageFocused)
{
image = mBgImageFocused;
}
@@ -1579,7 +1734,11 @@ void LLLineEditor::draw()
LLRect background( 0, getRect().getHeight(), getRect().getWidth(), 0 );
background.stretch( -mBorderThickness );
- S32 lineeditor_v_pad = llround((background.getHeight() - mGLFont->getLineHeight())/2);
+ S32 lineeditor_v_pad = (background.getHeight() - mGLFont->getLineHeight()) / 2;
+ if (mSpellCheck)
+ {
+ lineeditor_v_pad += 1;
+ }
drawBackground();
@@ -1654,14 +1813,14 @@ void LLLineEditor::draw()
{
S32 select_left;
S32 select_right;
- if( mSelectionStart < getCursor() )
+ if (mSelectionStart < mSelectionEnd)
{
select_left = mSelectionStart;
- select_right = getCursor();
+ select_right = mSelectionEnd;
}
else
{
- select_left = getCursor();
+ select_left = mSelectionEnd;
select_right = mSelectionStart;
}
@@ -1676,7 +1835,7 @@ void LLLineEditor::draw()
0,
LLFontGL::NO_SHADOW,
select_left - mScrollHPos,
- mTextRightEdge - llround(rendered_pixels_right),
+ mTextRightEdge - ll_round(rendered_pixels_right),
&rendered_pixels_right);
}
@@ -1686,8 +1845,8 @@ void LLLineEditor::draw()
color.setAlpha(alpha);
// selected middle
S32 width = mGLFont->getWidth(mText.getWString().c_str(), mScrollHPos + rendered_text, select_right - mScrollHPos - rendered_text);
- width = llmin(width, mTextRightEdge - llround(rendered_pixels_right));
- gl_rect_2d(llround(rendered_pixels_right), cursor_top, llround(rendered_pixels_right)+width, cursor_bottom, color);
+ width = llmin(width, mTextRightEdge - ll_round(rendered_pixels_right));
+ gl_rect_2d(ll_round(rendered_pixels_right), cursor_top, ll_round(rendered_pixels_right)+width, cursor_bottom, color);
LLColor4 tmp_color( 1.f - text_color.mV[0], 1.f - text_color.mV[1], 1.f - text_color.mV[2], alpha );
rendered_text += mGLFont->render(
@@ -1698,14 +1857,14 @@ void LLLineEditor::draw()
0,
LLFontGL::NO_SHADOW,
select_right - mScrollHPos - rendered_text,
- mTextRightEdge - llround(rendered_pixels_right),
+ mTextRightEdge - ll_round(rendered_pixels_right),
&rendered_pixels_right);
}
if( (rendered_pixels_right < (F32)mTextRightEdge) && (rendered_text < text_len) )
{
// unselected, right side
- mGLFont->render(
+ rendered_text += mGLFont->render(
mText, mScrollHPos + rendered_text,
rendered_pixels_right, text_bottom,
text_color,
@@ -1713,13 +1872,13 @@ void LLLineEditor::draw()
0,
LLFontGL::NO_SHADOW,
S32_MAX,
- mTextRightEdge - llround(rendered_pixels_right),
+ mTextRightEdge - ll_round(rendered_pixels_right),
&rendered_pixels_right);
}
}
else
{
- mGLFont->render(
+ rendered_text = mGLFont->render(
mText, mScrollHPos,
rendered_pixels_right, text_bottom,
text_color,
@@ -1727,13 +1886,108 @@ void LLLineEditor::draw()
0,
LLFontGL::NO_SHADOW,
S32_MAX,
- mTextRightEdge - llround(rendered_pixels_right),
+ mTextRightEdge - ll_round(rendered_pixels_right),
&rendered_pixels_right);
}
#if 1 // for when we're ready for image art.
mBorder->setVisible(FALSE); // no more programmatic art.
#endif
+ if ( (getSpellCheck()) && (mText.length() > 2) )
+ {
+ // Calculate start and end indices for the first and last visible word
+ U32 start = prevWordPos(mScrollHPos), end = nextWordPos(mScrollHPos + rendered_text);
+
+ if ( (mSpellCheckStart != start) || (mSpellCheckEnd != end) )
+ {
+ const LLWString& text = mText.getWString().substr(start, end);
+
+ // Find the start of the first word
+ U32 word_start = 0, word_end = 0;
+ while ( (word_start < text.length()) && (!LLStringOps::isAlpha(text[word_start])) )
+ {
+ word_start++;
+ }
+
+ // Iterate over all words in the text block and check them one by one
+ mMisspellRanges.clear();
+ while (word_start < text.length())
+ {
+ // Find the end of the current word (special case handling for "'" when it's used as a contraction)
+ word_end = word_start + 1;
+ while ( (word_end < text.length()) &&
+ ((LLWStringUtil::isPartOfWord(text[word_end])) ||
+ ((L'\'' == text[word_end]) && (word_end + 1 < text.length()) &&
+ (LLStringOps::isAlnum(text[word_end - 1])) && (LLStringOps::isAlnum(text[word_end + 1])))) )
+ {
+ word_end++;
+ }
+ if (word_end > text.length())
+ {
+ break;
+ }
+
+ // Don't process words shorter than 3 characters
+ std::string word = wstring_to_utf8str(text.substr(word_start, word_end - word_start));
+ if ( (word.length() >= 3) && (!LLSpellChecker::instance().checkSpelling(word)) )
+ {
+ mMisspellRanges.push_back(std::pair<U32, U32>(start + word_start, start + word_end));
+ }
+
+ // Find the start of the next word
+ word_start = word_end + 1;
+ while ( (word_start < text.length()) && (!LLWStringUtil::isPartOfWord(text[word_start])) )
+ {
+ word_start++;
+ }
+ }
+
+ mSpellCheckStart = start;
+ mSpellCheckEnd = end;
+ }
+
+ // Draw squiggly lines under any (visible) misspelled words
+ for (std::list<std::pair<U32, U32> >::const_iterator it = mMisspellRanges.begin(); it != mMisspellRanges.end(); ++it)
+ {
+ // Skip over words that aren't (partially) visible
+ if ( ((it->first < start) && (it->second < start)) || (it->first > end) )
+ {
+ continue;
+ }
+
+ // Skip the current word if the user is still busy editing it
+ if ( (!mSpellCheckTimer.hasExpired()) && (it->first <= (U32)mCursorPos) && (it->second >= (U32)mCursorPos) )
+ {
+ continue;
+ }
+
+ S32 pxWidth = getRect().getWidth();
+ S32 pxStart = findPixelNearestPos(it->first - getCursor());
+ if (pxStart > pxWidth)
+ {
+ continue;
+ }
+ S32 pxEnd = findPixelNearestPos(it->second - getCursor());
+ if (pxEnd > pxWidth)
+ {
+ pxEnd = pxWidth;
+ }
+
+ S32 pxBottom = (S32)(text_bottom + mGLFont->getDescenderHeight());
+
+ gGL.color4ub(255, 0, 0, 200);
+ while (pxStart + 1 < pxEnd)
+ {
+ gl_line_2d(pxStart, pxBottom, pxStart + 2, pxBottom - 2);
+ if (pxStart + 3 < pxEnd)
+ {
+ gl_line_2d(pxStart + 2, pxBottom - 3, pxStart + 4, pxBottom - 1);
+ }
+ pxStart += 4;
+ }
+ }
+ }
+
// If we're editing...
if( hasFocus())
{
@@ -1773,8 +2027,8 @@ void LLLineEditor::draw()
LLRect screen_pos = calcScreenRect();
LLCoordGL ime_pos( screen_pos.mLeft + pixels_after_scroll, screen_pos.mTop - lineeditor_v_pad );
- ime_pos.mX = (S32) (ime_pos.mX * LLUI::sGLScaleFactor.mV[VX]);
- ime_pos.mY = (S32) (ime_pos.mY * LLUI::sGLScaleFactor.mV[VY]);
+ ime_pos.mX = (S32) (ime_pos.mX * LLUI::getScaleFactor().mV[VX]);
+ ime_pos.mY = (S32) (ime_pos.mY * LLUI::getScaleFactor().mV[VY]);
getWindow()->setLanguageTextInput( ime_pos );
}
}
@@ -1792,7 +2046,7 @@ void LLLineEditor::draw()
0,
LLFontGL::NO_SHADOW,
S32_MAX,
- mTextRightEdge - llround(rendered_pixels_right),
+ mTextRightEdge - ll_round(rendered_pixels_right),
&rendered_pixels_right, FALSE);
}
@@ -1817,7 +2071,7 @@ void LLLineEditor::draw()
0,
LLFontGL::NO_SHADOW,
S32_MAX,
- mTextRightEdge - llround(rendered_pixels_right),
+ mTextRightEdge - ll_round(rendered_pixels_right),
&rendered_pixels_right, FALSE);
}
// Draw children (border)
@@ -1839,6 +2093,27 @@ S32 LLLineEditor::findPixelNearestPos(const S32 cursor_offset) const
return result;
}
+S32 LLLineEditor::calcCursorPos(S32 mouse_x)
+{
+ const llwchar* wtext = mText.getWString().c_str();
+ LLWString asterix_text;
+ if (mDrawAsterixes)
+ {
+ for (S32 i = 0; i < mText.length(); i++)
+ {
+ asterix_text += utf8str_to_wstring(PASSWORD_ASTERISK);
+ }
+ wtext = asterix_text.c_str();
+ }
+
+ S32 cur_pos = mScrollHPos +
+ mGLFont->charFromPixelOffset(
+ wtext, mScrollHPos,
+ (F32)(mouse_x - mTextLeftEdge),
+ (F32)(mTextRightEdge - mTextLeftEdge + 1)); // min-max range is inclusive
+
+ return cur_pos;
+}
//virtual
void LLLineEditor::clear()
{
@@ -1932,6 +2207,22 @@ void LLLineEditor::setPrevalidate(LLTextValidate::validate_func_t func)
updateAllowingLanguageInput();
}
+void LLLineEditor::setPrevalidateInput(LLTextValidate::validate_func_t func)
+{
+ mPrevalidateInputFunc = func;
+ updateAllowingLanguageInput();
+}
+
+bool LLLineEditor::prevalidateInput(const LLWString& wstr)
+{
+ if (mPrevalidateInputFunc && !mPrevalidateInputFunc(wstr))
+ {
+ return false;
+ }
+
+ return true;
+}
+
// static
BOOL LLLineEditor::postvalidateFloat(const std::string &str)
{
@@ -1991,6 +2282,32 @@ BOOL LLLineEditor::postvalidateFloat(const std::string &str)
return success;
}
+BOOL LLLineEditor::evaluateFloat()
+{
+ bool success;
+ F32 result = 0.f;
+ std::string expr = getText();
+ LLStringUtil::toUpper(expr);
+
+ success = LLCalc::getInstance()->evalString(expr, result);
+
+ if (!success)
+ {
+ // Move the cursor to near the error on failure
+ setCursor(LLCalc::getInstance()->getLastErrorPos());
+ // *TODO: Translated error message indicating the type of error? Select error text?
+ }
+ else
+ {
+ // Replace the expression with the result
+ std::string result_str = llformat("%f",result);
+ setText(result_str);
+ selectAll();
+ }
+
+ return success;
+}
+
void LLLineEditor::onMouseCaptureLost()
{
endSelection();
@@ -2002,6 +2319,15 @@ void LLLineEditor::setSelectAllonFocusReceived(BOOL b)
mSelectAllonFocusReceived = b;
}
+void LLLineEditor::onKeystroke()
+{
+ if (mKeystrokeCallback)
+ {
+ mKeystrokeCallback(this);
+ }
+
+ mSpellCheckStart = mSpellCheckEnd = -1;
+}
void LLLineEditor::setKeystrokeCallback(callback_t callback, void* user_data)
{
@@ -2057,7 +2383,7 @@ void LLLineEditor::resetPreedit()
{
if (hasPreeditString())
{
- llwarns << "Preedit and selection!" << llendl;
+ LL_WARNS() << "Preedit and selection!" << LL_ENDL;
deselect();
}
else
@@ -2124,10 +2450,9 @@ void LLLineEditor::updatePreedit(const LLWString &preedit_string,
// Update of the preedit should be caused by some key strokes.
mKeystrokeTimer.reset();
- if( mKeystrokeCallback )
- {
- mKeystrokeCallback( this );
- }
+ onKeystroke();
+
+ mSpellCheckTimer.setTimerExpirySec(SPELLCHECK_DELAY);
}
BOOL LLLineEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect *bounds, LLRect *control) const
@@ -2222,7 +2547,7 @@ void LLLineEditor::markAsPreedit(S32 position, S32 length)
setCursor(position);
if (hasPreeditString())
{
- llwarns << "markAsPreedit invoked when hasPreeditString is true." << llendl;
+ LL_WARNS() << "markAsPreedit invoked when hasPreeditString is true." << LL_ENDL;
}
mPreeditWString.assign( LLWString( mText.getWString(), position, length ) );
if (length > 0)
@@ -2250,7 +2575,7 @@ void LLLineEditor::markAsPreedit(S32 position, S32 length)
S32 LLLineEditor::getPreeditFontSize() const
{
- return llround(mGLFont->getLineHeight() * LLUI::sGLScaleFactor.mV[VY]);
+ return ll_round(mGLFont->getLineHeight() * LLUI::getScaleFactor().mV[VY]);
}
void LLLineEditor::setReplaceNewlinesWithSpaces(BOOL replace)
@@ -2279,7 +2604,38 @@ void LLLineEditor::showContextMenu(S32 x, S32 y)
S32 screen_x, screen_y;
localPointToScreen(x, y, &screen_x, &screen_y);
- menu->show(screen_x, screen_y);
+
+ setCursorAtLocalPos(x);
+ if (hasSelection())
+ {
+ if ( (mCursorPos < llmin(mSelectionStart, mSelectionEnd)) || (mCursorPos > llmax(mSelectionStart, mSelectionEnd)) )
+ {
+ deselect();
+ }
+ else
+ {
+ setCursor(llmax(mSelectionStart, mSelectionEnd));
+ }
+ }
+
+ bool use_spellcheck = getSpellCheck(), is_misspelled = false;
+ if (use_spellcheck)
+ {
+ mSuggestionList.clear();
+
+ // If the cursor is on a misspelled word, retrieve suggestions for it
+ std::string misspelled_word = getMisspelledWord(mCursorPos);
+ if ((is_misspelled = !misspelled_word.empty()) == true)
+ {
+ LLSpellChecker::instance().getSuggestions(misspelled_word, mSuggestionList);
+ }
+ }
+
+ menu->setItemVisible("Suggestion Separator", (use_spellcheck) && (!mSuggestionList.empty()));
+ menu->setItemVisible("Add to Dictionary", (use_spellcheck) && (is_misspelled));
+ menu->setItemVisible("Add to Ignore", (use_spellcheck) && (is_misspelled));
+ menu->setItemVisible("Spellcheck Separator", (use_spellcheck) && (is_misspelled));
+ menu->show(screen_x, screen_y, this);
}
}
@@ -2290,3 +2646,8 @@ void LLLineEditor::setContextMenu(LLContextMenu* new_context_menu)
else
mContextMenuHandle.markDead();
}
+
+void LLLineEditor::setFont(const LLFontGL* font)
+{
+ mGLFont = font;
+}
diff --git a/indra/llui/lllineeditor.h b/indra/llui/lllineeditor.h
index 723423a5b9..c6d472f59b 100644..100755
--- a/indra/llui/lllineeditor.h
+++ b/indra/llui/lllineeditor.h
@@ -40,7 +40,9 @@
#include "llframetimer.h"
#include "lleditmenuhandler.h"
+#include "llspellcheckmenuhandler.h"
#include "lluictrl.h"
+#include "lluiimage.h"
#include "lluistring.h"
#include "llviewborder.h"
@@ -53,13 +55,13 @@ class LLButton;
class LLContextMenu;
class LLLineEditor
-: public LLUICtrl, public LLEditMenuHandler, protected LLPreeditor
+: public LLUICtrl, public LLEditMenuHandler, protected LLPreeditor, public LLSpellCheckMenuHandler
{
public:
typedef boost::function<void (LLLineEditor* caller)> keystroke_callback_t;
- struct MaxLength : public LLInitParam::Choice<MaxLength>
+ struct MaxLength : public LLInitParam::ChoiceBlock<MaxLength>
{
Alternative<S32> bytes, chars;
@@ -75,6 +77,7 @@ public:
Optional<keystroke_callback_t> keystroke_callback;
Optional<LLTextValidate::validate_func_t, LLTextValidate::ValidateTextNamedFuncs> prevalidate_callback;
+ Optional<LLTextValidate::validate_func_t, LLTextValidate::ValidateTextNamedFuncs> prevalidate_input_callback;
Optional<LLViewBorder::Params> border;
@@ -84,8 +87,10 @@ public:
Optional<bool> select_on_focus,
revert_on_esc,
+ spellcheck,
commit_on_focus_lost,
ignore_tab,
+ bg_image_always_focused,
is_password;
// colors
@@ -144,6 +149,24 @@ public:
virtual void deselect();
virtual BOOL canDeselect() const;
+ // LLSpellCheckMenuHandler overrides
+ /*virtual*/ bool getSpellCheck() const;
+
+ /*virtual*/ const std::string& getSuggestion(U32 index) const;
+ /*virtual*/ U32 getSuggestionCount() const;
+ /*virtual*/ void replaceWithSuggestion(U32 index);
+
+ /*virtual*/ void addToDictionary();
+ /*virtual*/ bool canAddToDictionary() const;
+
+ /*virtual*/ void addToIgnore();
+ /*virtual*/ bool canAddToIgnore() const;
+
+ // Spell checking helper functions
+ std::string getMisspelledWord(U32 pos) const;
+ bool isMisspelledWord(U32 pos) const;
+ void onSpellCheckSettingsChange();
+
// view overrides
virtual void draw();
virtual void reshape(S32 width,S32 height,BOOL called_from_parent=TRUE);
@@ -201,6 +224,7 @@ public:
const LLColor4& getTentativeFgColor() const { return mTentativeFgColor.get(); }
const LLFontGL* getFont() const { return mGLFont; }
+ void setFont(const LLFontGL* font);
void setIgnoreArrowKeys(BOOL b) { mIgnoreArrowKeys = b; }
void setIgnoreTab(BOOL b) { mIgnoreTab = b; }
@@ -218,7 +242,9 @@ public:
void deleteSelection();
void setSelectAllonFocusReceived(BOOL b);
+ void setSelectAllonCommit(BOOL b) { mSelectAllonCommit = b; }
+ void onKeystroke();
typedef boost::function<void (LLLineEditor* caller, void* user_data)> callback_t;
void setKeystrokeCallback(callback_t callback, void* user_data);
@@ -230,8 +256,16 @@ public:
// Prevalidation controls which keystrokes can affect the editor
void setPrevalidate( LLTextValidate::validate_func_t func );
+ // This method sets callback that prevents from:
+ // - deleting, selecting, typing, cutting, pasting characters that are not valid.
+ // Also callback that this method sets differs from setPrevalidate in a way that it validates just inputed
+ // symbols, before existing text is modified, but setPrevalidate validates line after it was modified.
+ void setPrevalidateInput(LLTextValidate::validate_func_t func);
static BOOL postvalidateFloat(const std::string &str);
+ bool prevalidateInput(const LLWString& wstr);
+ BOOL evaluateFloat();
+
// line history support:
void setEnableLineHistory( BOOL enabled ) { mHaveHistory = enabled; } // switches line history on or off
void updateHistory(); // stores current line in history
@@ -249,6 +283,7 @@ private:
void addChar(const llwchar c);
void setCursorAtLocalPos(S32 local_mouse_x);
S32 findPixelNearestPos(S32 cursor_offset = 0) const;
+ S32 calcCursorPos(S32 mouse_x);
BOOL handleSpecialKey(KEY key, MASK mask);
BOOL handleSelectionKey(KEY key, MASK mask);
BOOL handleControlKey(KEY key, MASK mask);
@@ -309,7 +344,15 @@ protected:
S32 mLastSelectionStart;
S32 mLastSelectionEnd;
+ bool mSpellCheck;
+ S32 mSpellCheckStart;
+ S32 mSpellCheckEnd;
+ LLTimer mSpellCheckTimer;
+ std::list<std::pair<U32, U32> > mMisspellRanges;
+ std::vector<std::string> mSuggestionList;
+
LLTextValidate::validate_func_t mPrevalidateFunc;
+ LLTextValidate::validate_func_t mPrevalidateInputFunc;
LLFrameTimer mKeystrokeTimer;
LLTimer mTripleClickTimer;
@@ -328,10 +371,13 @@ protected:
BOOL mDrawAsterixes;
BOOL mSelectAllonFocusReceived;
+ BOOL mSelectAllonCommit;
BOOL mPassDelete;
BOOL mReadOnly;
+ BOOL mShowImageFocused;
+
LLWString mPreeditWString;
LLWString mPreeditOverwrittenWString;
std::vector<S32> mPreeditPositions;
diff --git a/indra/llui/llloadingindicator.cpp b/indra/llui/llloadingindicator.cpp
index 7b29d92ea0..1ede5b706f 100644..100755
--- a/indra/llui/llloadingindicator.cpp
+++ b/indra/llui/llloadingindicator.cpp
@@ -34,61 +34,28 @@
// Project includes
#include "lluictrlfactory.h"
#include "lluiimage.h"
+#include "boost/foreach.hpp"
// registered in llui.cpp to avoid being left out by MS linker
//static LLDefaultChildRegistry::Register<LLLoadingIndicator> r("loading_indicator");
///////////////////////////////////////////////////////////////////////////////
-// LLLoadingIndicator::Data class
+// LLLoadingIndicator class
///////////////////////////////////////////////////////////////////////////////
-/**
- * Pre-loaded images shared by all instances of the widget
- */
-class LLLoadingIndicator::Data: public LLSingleton<LLLoadingIndicator::Data>
+LLLoadingIndicator::LLLoadingIndicator(const Params& p)
+: LLUICtrl(p),
+ mImagesPerSec(p.images_per_sec > 0 ? p.images_per_sec : 1.0f),
+ mCurImageIdx(0)
{
-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()
+void LLLoadingIndicator::initFromParams(const Params& p)
{
- // Load images.
- for (U8 i = 0; i < NIMAGES; ++i)
+ BOOST_FOREACH(LLUIImage* image, p.images().image)
{
- std::string img_name = llformat("Progress_%d", i+1);
- mImages[i] = LLUI::getUIImage(img_name, 0);
- llassert(mImages[i]);
+ mImages.push_back(image);
}
-}
-
-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();
@@ -100,16 +67,21 @@ void LLLoadingIndicator::draw()
if (mImageSwitchTimer.getStarted() && mImageSwitchTimer.hasExpired())
{
// Switch to the next image.
- mCurImagep = Data::instance().getNextImage(mCurImageIdx);
+ if (!mImages.empty())
+ {
+ mCurImageIdx = (mCurImageIdx + 1) % mImages.size();
+ }
// Restart timer.
start();
}
+ LLUIImagePtr cur_image = mImages.empty() ? LLUIImagePtr(NULL) : mImages[mCurImageIdx];
+
// Draw current image.
- if( mCurImagep.notNull() )
+ if( cur_image.notNull() )
{
- mCurImagep->draw(getLocalRect(), LLColor4::white % getDrawContext().mAlpha);
+ cur_image->draw(getLocalRect(), LLColor4::white % getDrawContext().mAlpha);
}
LLUICtrl::draw();
@@ -123,6 +95,6 @@ void LLLoadingIndicator::stop()
void LLLoadingIndicator::start()
{
mImageSwitchTimer.start();
- F32 period = 1.0f / (Data::instance().getImagesCount() * mRotationsPerSec);
+ F32 period = 1.0f / (mImages.size() * mImagesPerSec);
mImageSwitchTimer.setTimerExpirySec(period);
}
diff --git a/indra/llui/llloadingindicator.h b/indra/llui/llloadingindicator.h
index 4e4a224ef6..ffcb329f42 100644..100755
--- a/indra/llui/llloadingindicator.h
+++ b/indra/llui/llloadingindicator.h
@@ -28,6 +28,7 @@
#define LL_LLLOADINGINDICATOR_H
#include "lluictrl.h"
+#include "lluiimage.h"
///////////////////////////////////////////////////////////////////////////////
// class LLLoadingIndicator
@@ -36,8 +37,8 @@
/**
* Perpetual loading indicator (a la MacOSX or YouTube)
*
- * Number of rotations per second can be overriden
- * with the "roations_per_sec" parameter.
+ * Number of rotations per second can be overridden
+ * with the "images_per_sec" parameter.
*
* Can start/stop spinning.
*
@@ -49,11 +50,24 @@ class LLLoadingIndicator
{
LOG_CLASS(LLLoadingIndicator);
public:
+
+ struct Images : public LLInitParam::Block<Images>
+ {
+ Multiple<LLUIImage*> image;
+
+ Images()
+ : image("image")
+ {}
+ };
+
struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
{
- Optional<F32> rotations_per_sec;
+ Optional<F32> images_per_sec;
+ Optional<Atomic<Images> > images;
+
Params()
- : rotations_per_sec("rotations_per_sec", 1.0f)
+ : images_per_sec("images_per_sec", 1.0f),
+ images("images")
{}
};
@@ -72,16 +86,19 @@ public:
*/
void start();
+ void reset() { mCurImageIdx = 0; }
+
private:
LLLoadingIndicator(const Params&);
- friend class LLUICtrlFactory;
+ void initFromParams(const Params&);
- class Data;
+ friend class LLUICtrlFactory;
- F32 mRotationsPerSec;
+ F32 mImagesPerSec;
S8 mCurImageIdx;
- LLPointer<LLUIImage> mCurImagep;
LLFrameTimer mImageSwitchTimer;
+
+ std::vector<LLUIImagePtr> mImages;
};
#endif // LL_LLLOADINGINDICATOR_H
diff --git a/indra/llui/lllocalcliprect.cpp b/indra/llui/lllocalcliprect.cpp
index 6841301219..f3a526faeb 100644..100755
--- a/indra/llui/lllocalcliprect.cpp
+++ b/indra/llui/lllocalcliprect.cpp
@@ -88,10 +88,10 @@ void LLScreenClipRect::updateScissorRegion()
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;
+ x = llfloor(rect.mLeft * LLUI::getScaleFactor().mV[VX]);
+ y = llfloor(rect.mBottom * LLUI::getScaleFactor().mV[VY]);
+ w = llmax(0, llceil(rect.getWidth() * LLUI::getScaleFactor().mV[VX])) + 1;
+ h = llmax(0, llceil(rect.getHeight() * LLUI::getScaleFactor().mV[VY])) + 1;
glScissor( x,y,w,h );
stop_glerror();
}
diff --git a/indra/llui/lllocalcliprect.h b/indra/llui/lllocalcliprect.h
index eeeaf2adb6..eeeaf2adb6 100644..100755
--- a/indra/llui/lllocalcliprect.h
+++ b/indra/llui/lllocalcliprect.h
diff --git a/indra/llui/llmenubutton.cpp b/indra/llui/llmenubutton.cpp
index eed0085273..303afcda15 100644..100755
--- a/indra/llui/llmenubutton.cpp
+++ b/indra/llui/llmenubutton.cpp
@@ -35,35 +35,36 @@
static LLDefaultChildRegistry::Register<LLMenuButton> r("menu_button");
+void LLMenuButton::MenuPositions::declareValues()
+{
+ declare("topleft", MP_TOP_LEFT);
+ declare("topright", MP_TOP_RIGHT);
+ declare("bottomleft", MP_BOTTOM_LEFT);
+}
LLMenuButton::Params::Params()
-: menu_filename("menu_filename")
+: menu_filename("menu_filename"),
+ position("menu_position", MP_BOTTOM_LEFT)
{
+ addSynonym(position, "position");
}
LLMenuButton::LLMenuButton(const LLMenuButton::Params& p)
: LLButton(p),
mIsMenuShown(false),
- mMenuPosition(MP_BOTTOM_LEFT)
+ mMenuPosition(p.position),
+ mOwnMenu(false)
{
std::string menu_filename = p.menu_filename;
- if (!menu_filename.empty())
- {
- LLToggleableMenu* menu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>(menu_filename, LLMenuGL::sMenuContainer, LLMenuHolderGL::child_registry_t::instance());
- if (!menu)
- {
- llwarns << "Error loading menu_button menu" << llendl;
- return;
- }
-
- menu->setVisibilityChangeCallback(boost::bind(&LLMenuButton::onMenuVisibilityChange, this, _2));
-
- mMenuHandle = menu->getHandle();
+ setMenu(menu_filename, mMenuPosition);
+ updateMenuOrigin();
+}
- updateMenuOrigin();
- }
+LLMenuButton::~LLMenuButton()
+{
+ cleanup();
}
boost::signals2::connection LLMenuButton::setMouseDownCallback( const mouse_signal_t::slot_type& cb )
@@ -73,9 +74,7 @@ boost::signals2::connection LLMenuButton::setMouseDownCallback( const mouse_sign
void LLMenuButton::hideMenu()
{
- if(mMenuHandle.isDead()) return;
-
- LLToggleableMenu* menu = dynamic_cast<LLToggleableMenu*>(mMenuHandle.get());
+ LLToggleableMenu* menu = getMenu();
if (menu)
{
menu->setVisible(FALSE);
@@ -87,19 +86,40 @@ LLToggleableMenu* LLMenuButton::getMenu()
return dynamic_cast<LLToggleableMenu*>(mMenuHandle.get());
}
-void LLMenuButton::setMenu(LLToggleableMenu* menu, EMenuPosition position /*MP_TOP_LEFT*/)
+void LLMenuButton::setMenu(const std::string& menu_filename, EMenuPosition position /*MP_TOP_LEFT*/)
+{
+ if (menu_filename.empty())
+ {
+ return;
+ }
+
+ llassert(LLMenuGL::sMenuContainer != NULL);
+ LLToggleableMenu* menu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>(menu_filename, LLMenuGL::sMenuContainer, LLMenuHolderGL::child_registry_t::instance());
+ if (!menu)
+ {
+ LL_WARNS() << "Error loading menu_button menu" << LL_ENDL;
+ return;
+ }
+
+ setMenu(menu, position, true);
+}
+
+void LLMenuButton::setMenu(LLToggleableMenu* menu, EMenuPosition position /*MP_TOP_LEFT*/, bool take_ownership /*false*/)
{
if (!menu) return;
+ cleanup(); // destroy the previous memnu if we own it
+
mMenuHandle = menu->getHandle();
mMenuPosition = position;
+ mOwnMenu = take_ownership;
menu->setVisibilityChangeCallback(boost::bind(&LLMenuButton::onMenuVisibilityChange, this, _2));
}
BOOL LLMenuButton::handleKeyHere(KEY key, MASK mask )
{
- if (mMenuHandle.isDead()) return FALSE;
+ if (!getMenu()) return FALSE;
if( KEY_RETURN == key && mask == MASK_NONE && !gKeyboard->getKeyRepeated(key))
{
@@ -111,7 +131,7 @@ BOOL LLMenuButton::handleKeyHere(KEY key, MASK mask )
return TRUE;
}
- LLToggleableMenu* menu = dynamic_cast<LLToggleableMenu*>(mMenuHandle.get());
+ LLToggleableMenu* menu = getMenu();
if (menu && menu->getVisible() && key == KEY_ESCAPE && mask == MASK_NONE)
{
menu->setVisible(FALSE);
@@ -132,9 +152,12 @@ BOOL LLMenuButton::handleMouseDown(S32 x, S32 y, MASK mask)
void LLMenuButton::toggleMenu()
{
- if(mMenuHandle.isDead()) return;
+ if (mValidateSignal && !(*mValidateSignal)(this, LLSD()))
+ {
+ return;
+ }
- LLToggleableMenu* menu = dynamic_cast<LLToggleableMenu*>(mMenuHandle.get());
+ LLToggleableMenu* menu = getMenu();
if (!menu) return;
// Store the button rectangle to toggle menu visibility if a mouse event
@@ -163,7 +186,8 @@ void LLMenuButton::toggleMenu()
void LLMenuButton::updateMenuOrigin()
{
- if (mMenuHandle.isDead()) return;
+ LLToggleableMenu* menu = getMenu();
+ if (!menu) return;
LLRect rect = getRect();
@@ -172,12 +196,12 @@ void LLMenuButton::updateMenuOrigin()
case MP_TOP_LEFT:
{
mX = rect.mLeft;
- mY = rect.mTop + mMenuHandle.get()->getRect().getHeight();
+ mY = rect.mTop + menu->getRect().getHeight();
break;
}
case MP_TOP_RIGHT:
{
- const LLRect& menu_rect = mMenuHandle.get()->getRect();
+ const LLRect& menu_rect = menu->getRect();
mX = rect.mRight - menu_rect.getWidth();
mY = rect.mTop + menu_rect.getHeight();
break;
@@ -204,3 +228,11 @@ void LLMenuButton::onMenuVisibilityChange(const LLSD& param)
mIsMenuShown = false;
}
}
+
+void LLMenuButton::cleanup()
+{
+ if (mMenuHandle.get() && mOwnMenu)
+ {
+ mMenuHandle.get()->die();
+ }
+}
diff --git a/indra/llui/llmenubutton.h b/indra/llui/llmenubutton.h
index 7b657595da..67ec1983b3 100644..100755
--- a/indra/llui/llmenubutton.h
+++ b/indra/llui/llmenubutton.h
@@ -34,22 +34,33 @@ class LLToggleableMenu;
class LLMenuButton
: public LLButton
{
+ LOG_CLASS(LLMenuButton);
+
public:
+ typedef enum e_menu_position
+ {
+ MP_TOP_LEFT,
+ MP_TOP_RIGHT,
+ MP_BOTTOM_LEFT
+ } EMenuPosition;
+
+ struct MenuPositions
+ : public LLInitParam::TypeValuesHelper<EMenuPosition, MenuPositions>
+ {
+ static void declareValues();
+ };
+
struct Params
: public LLInitParam::Block<Params, LLButton::Params>
{
// filename for it's toggleable menu
Optional<std::string> menu_filename;
+ Optional<EMenuPosition, MenuPositions> position;
Params();
};
- typedef enum e_menu_position
- {
- MP_TOP_LEFT,
- MP_TOP_RIGHT,
- MP_BOTTOM_LEFT
- } EMenuPosition;
+
boost::signals2::connection setMouseDownCallback( const mouse_signal_t::slot_type& cb );
@@ -59,13 +70,15 @@ public:
void hideMenu();
LLToggleableMenu* getMenu();
- void setMenu(LLToggleableMenu* menu, EMenuPosition position = MP_TOP_LEFT);
+ void setMenu(const std::string& menu_filename, EMenuPosition position = MP_TOP_LEFT);
+ void setMenu(LLToggleableMenu* menu, EMenuPosition position = MP_TOP_LEFT, bool take_ownership = false);
void setMenuPosition(EMenuPosition position) { mMenuPosition = position; }
protected:
friend class LLUICtrlFactory;
LLMenuButton(const Params&);
+ ~LLMenuButton();
void toggleMenu();
void updateMenuOrigin();
@@ -73,11 +86,14 @@ protected:
void onMenuVisibilityChange(const LLSD& param);
private:
+ void cleanup();
+
LLHandle<LLView> mMenuHandle;
bool mIsMenuShown;
EMenuPosition mMenuPosition;
S32 mX;
S32 mY;
+ bool mOwnMenu; // true if we manage the menu lifetime
};
diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp
index 32d7be377a..7cdbcb0621 100644..100755
--- a/indra/llui/llmenugl.cpp
+++ b/indra/llui/llmenugl.cpp
@@ -60,9 +60,11 @@
#include "v2math.h"
#include <set>
#include <boost/tokenizer.hpp>
+#include <boost/foreach.hpp>
// static
LLMenuHolderGL *LLMenuGL::sMenuContainer = NULL;
+view_listener_t::listener_map_t view_listener_t::sListeners;
S32 MENU_BAR_HEIGHT = 0;
S32 MENU_BAR_WIDTH = 0;
@@ -90,7 +92,6 @@ const S32 TEAROFF_SEPARATOR_HEIGHT_PIXELS = 10;
const S32 MENU_ITEM_PADDING = 4;
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
@@ -100,17 +101,10 @@ const std::string LLMenuGL::ARROW_DOWN("vvvvvvv");
const F32 MAX_MOUSE_SLOPE_SUB_MENU = 0.9f;
-const S32 PIE_GESTURE_ACTIVATE_DISTANCE = 10;
-
BOOL LLMenuGL::sKeyboardMode = FALSE;
LLHandle<LLView> LLMenuHolderGL::sItemLastSelectedHandle;
LLFrameTimer LLMenuHolderGL::sItemActivationTimer;
-//LLColor4 LLMenuGL::sBackgroundColor( 0.8f, 0.8f, 0.0f, 1.0f );
-
-const S32 PIE_CENTER_SIZE = 20; // pixels, radius of center hole
-const F32 PIE_SCALE_FACTOR = 1.7f; // scale factor for pie menu when mouse is initially down
-const F32 PIE_SHRINK_TIME = 0.2f; // time of transition between unbounded and bounded display of pie menu
const F32 ACTIVATE_HIGHLIGHT_TIME = 0.3f;
@@ -149,7 +143,7 @@ LLMenuItemGL::Params::Params()
highlight_bg_color("highlight_bg_color"),
highlight_fg_color("highlight_fg_color")
{
- mouse_opaque = true;
+ changeDefault(mouse_opaque, true);
}
// Default constructor
@@ -281,7 +275,7 @@ BOOL LLMenuItemGL::addToAcceleratorList(std::list <LLKeyBinding*> *listp)
// warning.append("\n ");
// warning.append(mLabel);
- // llwarns << warning << llendl;
+ // LL_WARNS() << warning << LL_ENDL;
// LLAlertDialog::modalAlert(warning);
return FALSE;
}
@@ -318,7 +312,7 @@ void LLMenuItemGL::setJumpKey(KEY key)
// virtual
U32 LLMenuItemGL::getNominalHeight( void ) const
{
- return llround(mFont->getLineHeight()) + MENU_ITEM_PADDING;
+ return mFont->getLineHeight() + MENU_ITEM_PADDING;
}
//virtual
@@ -509,19 +503,19 @@ void LLMenuItemGL::draw( void )
{
if( !mDrawBoolLabel.empty() )
{
- mFont->render( mDrawBoolLabel.getWString(), 0, (F32)LEFT_PAD_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f) + 1.f, color,
+ mFont->render( mDrawBoolLabel.getWString(), 0, (F32)LEFT_PAD_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f), color,
LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE );
}
- mFont->render( mLabel.getWString(), 0, (F32)LEFT_PLAIN_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f) + 1.f, color,
+ mFont->render( mLabel.getWString(), 0, (F32)LEFT_PLAIN_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f), color,
LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE );
if( !mDrawAccelLabel.empty() )
{
- mFont->render( mDrawAccelLabel.getWString(), 0, (F32)getRect().mRight - (F32)RIGHT_PLAIN_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f) + 1.f, color,
+ mFont->render( mDrawAccelLabel.getWString(), 0, (F32)getRect().mRight - (F32)RIGHT_PLAIN_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f), color,
LLFontGL::RIGHT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE );
}
if( !mDrawBranchLabel.empty() )
{
- mFont->render( mDrawBranchLabel.getWString(), 0, (F32)getRect().mRight - (F32)RIGHT_PAD_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f) + 1.f, color,
+ mFont->render( mDrawBranchLabel.getWString(), 0, (F32)getRect().mRight - (F32)RIGHT_PAD_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f), color,
LLFontGL::RIGHT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE );
}
}
@@ -550,13 +544,13 @@ BOOL LLMenuItemGL::setLabelArg( const std::string& key, const LLStringExplicit&
return TRUE;
}
-void LLMenuItemGL::handleVisibilityChange(BOOL new_visibility)
+void LLMenuItemGL::onVisibilityChange(BOOL new_visibility)
{
if (getMenu())
{
getMenu()->needsArrange();
}
- LLView::handleVisibilityChange(new_visibility);
+ LLView::onVisibilityChange(new_visibility);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -566,8 +560,6 @@ void LLMenuItemGL::handleVisibilityChange(BOOL new_visibility)
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
LLMenuItemSeparatorGL::Params::Params()
{
- name = "separator";
- label = SEPARATOR_LABEL;
}
LLMenuItemSeparatorGL::LLMenuItemSeparatorGL(const LLMenuItemSeparatorGL::Params& p) :
@@ -596,12 +588,12 @@ BOOL LLMenuItemSeparatorGL::handleMouseDown(S32 x, S32 y, MASK mask)
{
// the menu items are in the child list in bottom up order
LLView* prev_menu_item = parent_menu->findNextSibling(this);
- return prev_menu_item ? prev_menu_item->handleMouseDown(x, prev_menu_item->getRect().getHeight(), mask) : FALSE;
+ return (prev_menu_item && prev_menu_item->getVisible() && prev_menu_item->getEnabled()) ? prev_menu_item->handleMouseDown(x, prev_menu_item->getRect().getHeight(), mask) : FALSE;
}
else
{
LLView* next_menu_item = parent_menu->findPrevSibling(this);
- return next_menu_item ? next_menu_item->handleMouseDown(x, 0, mask) : FALSE;
+ return (next_menu_item && next_menu_item->getVisible() && next_menu_item->getEnabled()) ? next_menu_item->handleMouseDown(x, 0, mask) : FALSE;
}
}
@@ -611,12 +603,12 @@ BOOL LLMenuItemSeparatorGL::handleMouseUp(S32 x, S32 y, MASK mask)
if (y > getRect().getHeight() / 2)
{
LLView* prev_menu_item = parent_menu->findNextSibling(this);
- return prev_menu_item ? prev_menu_item->handleMouseUp(x, prev_menu_item->getRect().getHeight(), mask) : FALSE;
+ return (prev_menu_item && prev_menu_item->getVisible() && prev_menu_item->getEnabled()) ? prev_menu_item->handleMouseUp(x, prev_menu_item->getRect().getHeight(), mask) : FALSE;
}
else
{
LLView* next_menu_item = parent_menu->findPrevSibling(this);
- return next_menu_item ? next_menu_item->handleMouseUp(x, 0, mask) : FALSE;
+ return (next_menu_item && next_menu_item->getVisible() && next_menu_item->getEnabled()) ? next_menu_item->handleMouseUp(x, 0, mask) : FALSE;
}
}
@@ -696,8 +688,11 @@ void LLMenuItemTearOffGL::onCommit()
{
if (getMenu()->getTornOff())
{
- LLTearOffMenu* torn_off_menu = (LLTearOffMenu*)(getMenu()->getParent());
- torn_off_menu->closeFloater();
+ LLTearOffMenu * torn_off_menu = dynamic_cast<LLTearOffMenu*>(getMenu()->getParent());
+ if (torn_off_menu)
+ {
+ torn_off_menu->closeFloater();
+ }
}
else
{
@@ -755,30 +750,6 @@ U32 LLMenuItemTearOffGL::getNominalHeight( void ) const
return TEAROFF_SEPARATOR_HEIGHT_PIXELS;
}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Class LLMenuItemBlankGL
-//
-// This class represents a blank, non-functioning item.
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-class LLMenuItemBlankGL : public LLMenuItemGL
-{
-public:
- struct Params : public LLInitParam::Block<Params, LLMenuItemGL::Params>
- {
- Params()
- {
- name="";
- enabled = false;
- }
- };
- LLMenuItemBlankGL( const Params& p ) : LLMenuItemGL( p )
- {}
- virtual void draw( void ) {}
-};
-
-
///============================================================================
/// Class LLMenuItemCallGL
///============================================================================
@@ -974,9 +945,14 @@ LLMenuItemBranchGL::LLMenuItemBranchGL(const LLMenuItemBranchGL::Params& p)
LLMenuItemBranchGL::~LLMenuItemBranchGL()
{
- LLView::deleteViewByHandle(mBranchHandle);
+ if (mBranchHandle.get())
+ {
+ mBranchHandle.get()->die();
+ }
}
+
+
// virtual
LLView* LLMenuItemBranchGL::getChildView(const std::string& name, BOOL recurse) const
{
@@ -1119,7 +1095,8 @@ void LLMenuItemBranchGL::setHighlight( BOOL highlight )
BOOL auto_open = getEnabled() && (!branch->getVisible() || branch->getTornOff());
// torn off menus don't open sub menus on hover unless they have focus
- if (getMenu()->getTornOff() && !((LLFloater*)getMenu()->getParent())->hasFocus())
+ LLFloater * menu_parent = dynamic_cast<LLFloater *>(getMenu()->getParent());
+ if (getMenu()->getTornOff() && menu_parent && !menu_parent->hasFocus())
{
auto_open = FALSE;
}
@@ -1140,7 +1117,11 @@ void LLMenuItemBranchGL::setHighlight( BOOL highlight )
{
if (branch->getTornOff())
{
- ((LLFloater*)branch->getParent())->setFocus(FALSE);
+ LLFloater * branch_parent = dynamic_cast<LLFloater *>(branch->getParent());
+ if (branch_parent)
+ {
+ branch_parent->setFocus(FALSE);
+ }
branch->clearHoverItem();
}
else
@@ -1168,13 +1149,13 @@ void LLMenuItemBranchGL::updateBranchParent(LLView* parentp)
}
}
-void LLMenuItemBranchGL::handleVisibilityChange( BOOL new_visibility )
+void LLMenuItemBranchGL::onVisibilityChange( BOOL new_visibility )
{
if (new_visibility == FALSE && getBranch() && !getBranch()->getTornOff())
{
getBranch()->setVisible(FALSE);
}
- LLMenuItemGL::handleVisibilityChange(new_visibility);
+ LLMenuItemGL::onVisibilityChange(new_visibility);
}
BOOL LLMenuItemBranchGL::handleKeyHere( KEY key, MASK mask )
@@ -1197,11 +1178,19 @@ BOOL LLMenuItemBranchGL::handleKeyHere( KEY key, MASK mask )
BOOL handled = branch->clearHoverItem();
if (branch->getTornOff())
{
- ((LLFloater*)branch->getParent())->setFocus(FALSE);
+ LLFloater * branch_parent = dynamic_cast<LLFloater *>(branch->getParent());
+ if (branch_parent)
+ {
+ branch_parent->setFocus(FALSE);
+ }
}
if (handled && getMenu()->getTornOff())
{
- ((LLFloater*)getMenu()->getParent())->setFocus(TRUE);
+ LLFloater * menu_parent = dynamic_cast<LLFloater *>(getMenu()->getParent());
+ if (menu_parent)
+ {
+ menu_parent->setFocus(TRUE);
+ }
}
return handled;
}
@@ -1241,9 +1230,13 @@ void LLMenuItemBranchGL::openMenu()
if (branch->getTornOff())
{
- gFloaterView->bringToFront((LLFloater*)branch->getParent());
- // this might not be necessary, as torn off branches don't get focus and hence no highligth
- branch->highlightNextItem(NULL);
+ LLFloater * branch_parent = dynamic_cast<LLFloater *>(branch->getParent());
+ if (branch_parent)
+ {
+ gFloaterView->bringToFront(branch_parent);
+ // this might not be necessary, as torn off branches don't get focus and hence no highligth
+ branch->highlightNextItem(NULL);
+ }
}
else if( !branch->getVisible() )
{
@@ -1277,7 +1270,15 @@ void LLMenuItemBranchGL::openMenu()
{
// 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();
+ if (y + 2 * branch_rect.getHeight() - getRect().getHeight() > menu_region_rect.mTop)
+ {
+ // overlaps with top border, align with top
+ delta_y = menu_region_rect.mTop - y - branch_rect.getHeight();
+ }
+ else
+ {
+ delta_y = branch_rect.getHeight() - getRect().getHeight();
+ }
}
if( x + branch_rect.getWidth() > menu_region_rect.mRight )
@@ -1370,7 +1371,11 @@ void LLMenuItemBranchDownGL::openMenu( void )
{
if (branch->getTornOff())
{
- gFloaterView->bringToFront((LLFloater*)branch->getParent());
+ LLFloater * branch_parent = dynamic_cast<LLFloater *>(branch->getParent());
+ if (branch_parent)
+ {
+ gFloaterView->bringToFront(branch_parent);
+ }
}
else
{
@@ -1425,7 +1430,11 @@ void LLMenuItemBranchDownGL::setHighlight( BOOL highlight )
{
if (branch->getTornOff())
{
- ((LLFloater*)branch->getParent())->setFocus(FALSE);
+ LLFloater * branch_parent = dynamic_cast<LLFloater *>(branch->getParent());
+ if (branch_parent)
+ {
+ branch_parent->setFocus(FALSE);
+ }
branch->clearHoverItem();
}
else
@@ -1573,7 +1582,7 @@ void LLMenuItemBranchDownGL::draw( void )
std::string::size_type offset = upper_case_label.find(getJumpKey());
if (offset != std::string::npos)
{
- S32 x_offset = llround((F32)getRect().getWidth() / 2.f - getFont()->getWidthF32(mLabel.getString(), 0, S32_MAX) / 2.f);
+ S32 x_offset = ll_round((F32)getRect().getWidth() / 2.f - getFont()->getWidthF32(mLabel.getString(), 0, S32_MAX) / 2.f);
S32 x_begin = x_offset + getFont()->getWidth(mLabel, 0, offset);
S32 x_end = x_offset + getFont()->getWidth(mLabel, 0, offset + 1);
gl_line_2d(x_begin, LABEL_BOTTOM_PAD_PIXELS, x_end, LABEL_BOTTOM_PAD_PIXELS);
@@ -1713,7 +1722,8 @@ LLMenuGL::LLMenuGL(const LLMenuGL::Params& p)
mSpilloverMenu(NULL),
mJumpKey(p.jump_key),
mCreateJumpKeys(p.create_jump_keys),
- mNeedsArrange(FALSE),
+ mNeedsArrange(FALSE),
+ mResetScrollPositionOnShow(true),
mShortcutPad(p.shortcut_pad)
{
typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
@@ -1758,7 +1768,7 @@ void LLMenuGL::setCanTearOff(BOOL tear_off)
{
LLMenuItemTearOffGL::Params p;
mTearOffItem = LLUICtrlFactory::create<LLMenuItemTearOffGL>(p);
- addChildInBack(mTearOffItem);
+ addChild(mTearOffItem);
}
else if (!tear_off && mTearOffItem != NULL)
{
@@ -1772,16 +1782,50 @@ void LLMenuGL::setCanTearOff(BOOL tear_off)
bool LLMenuGL::addChild(LLView* view, S32 tab_group)
{
- if (LLMenuGL* menup = dynamic_cast<LLMenuGL*>(view))
+ LLMenuGL* menup = dynamic_cast<LLMenuGL*>(view);
+ if (menup)
+ {
+ return appendMenu(menup);
+ }
+
+ LLMenuItemGL* itemp = dynamic_cast<LLMenuItemGL*>(view);
+ if (itemp)
+ {
+ return append(itemp);
+ }
+
+ return false;
+}
+
+// Used in LLContextMenu and in LLTogleableMenu
+
+// Add an item to the context menu branch
+bool LLMenuGL::addContextChild(LLView* view, S32 tab_group)
+{
+ LLContextMenu* context = dynamic_cast<LLContextMenu*>(view);
+ if (context)
+ {
+ return appendContextSubMenu(context);
+ }
+
+ LLMenuItemSeparatorGL* separator = dynamic_cast<LLMenuItemSeparatorGL*>(view);
+ if (separator)
+ {
+ return append(separator);
+ }
+
+ LLMenuItemGL* item = dynamic_cast<LLMenuItemGL*>(view);
+ if (item)
{
- appendMenu(menup);
- return true;
+ return append(item);
}
- else if (LLMenuItemGL* itemp = dynamic_cast<LLMenuItemGL*>(view))
+
+ LLMenuGL* menup = dynamic_cast<LLMenuGL*>(view);
+ if (menup)
{
- append(itemp);
- return true;
+ return appendMenu(menup);
}
+
return false;
}
@@ -1813,20 +1857,28 @@ BOOL LLMenuGL::jumpKeysActive()
{
LLMenuItemGL* highlighted_item = getHighlightedItem();
BOOL active = getVisible() && getEnabled();
- if (getTornOff())
- {
- // activation of jump keys on torn off menus controlled by keyboard focus
- active = active && ((LLFloater*)getParent())->hasFocus();
- }
- else
+ if (active)
{
- // Are we the terminal active menu?
- // Yes, if parent menu item deems us to be active (just being visible is sufficient for top-level menus)
- // and we don't have a highlighted menu item pointing to an active sub-menu
- active = active && (!getParentMenuItem() || getParentMenuItem()->isActive()) // I have a parent that is active...
- && (!highlighted_item || !highlighted_item->isActive()); //... but no child that is active
+ if (getTornOff())
+ {
+ // activation of jump keys on torn off menus controlled by keyboard focus
+ LLFloater * parent = dynamic_cast<LLFloater *>(getParent());
+ if (parent)
+ {
+ active = parent->hasFocus();
+ }
+ }
+ else
+ {
+ // Are we the terminal active menu?
+ // Yes, if parent menu item deems us to be active (just being visible is sufficient for top-level menus)
+ // and we don't have a highlighted menu item pointing to an active sub-menu
+ active = (!getParentMenuItem() || getParentMenuItem()->isActive()) // I have a parent that is active...
+ && (!highlighted_item || !highlighted_item->isActive()); //... but no child that is active
+ }
}
+
return active;
}
@@ -1842,7 +1894,12 @@ BOOL LLMenuGL::isOpen()
return TRUE;
}
// otherwise we are only active if we have keyboard focus
- return ((LLFloater*)getParent())->hasFocus();
+ LLFloater * parent = dynamic_cast<LLFloater *>(getParent());
+ if (parent)
+ {
+ return parent->hasFocus();
+ }
+ return FALSE;
}
else
{
@@ -1936,14 +1993,20 @@ bool LLMenuGL::scrollItems(EScrollingDirection direction)
{
item_list_t::reverse_iterator first_visible_item_iter = mItems.rend();
+ // Need to scroll through number of actual existing items in menu.
+ // Otherwise viewer will hang for a time needed to scroll U32_MAX
+ // times in std::advance(). STORM-659.
+ size_t nitems = mItems.size();
+ U32 scrollable_items = nitems < mMaxScrollableItems ? nitems : mMaxScrollableItems;
+
// Advance by mMaxScrollableItems back from the end of the list
// to make the last item visible.
- std::advance(first_visible_item_iter, mMaxScrollableItems);
+ std::advance(first_visible_item_iter, scrollable_items);
mFirstVisibleItem = *first_visible_item_iter;
break;
}
default:
- llwarns << "Unknown scrolling direction: " << direction << llendl;
+ LL_WARNS() << "Unknown scrolling direction: " << direction << LL_ENDL;
}
mNeedsArrange = TRUE;
@@ -1969,19 +2032,11 @@ void LLMenuGL::arrange( void )
// torn off menus are not constrained to the size of the screen
U32 max_width = getTornOff() ? U32_MAX : menu_region_rect.getWidth();
- U32 max_height = U32_MAX;
- if (!getTornOff())
- {
- max_height = getRect().mTop - menu_region_rect.mBottom;
- if (menu_region_rect.mTop - getRect().mTop > (S32)max_height)
- {
- max_height = menu_region_rect.mTop - getRect().mTop;
- }
- }
+ U32 max_height = getTornOff() ? U32_MAX: menu_region_rect.getHeight();
// *FIX: create the item first and then ask for its dimensions?
S32 spillover_item_width = PLAIN_PAD_PIXELS + LLFontGL::getFontSansSerif()->getWidth( std::string("More") ); // *TODO: Translate
- S32 spillover_item_height = llround(LLFontGL::getFontSansSerif()->getLineHeight()) + MENU_ITEM_PADDING;
+ S32 spillover_item_height = LLFontGL::getFontSansSerif()->getLineHeight() + MENU_ITEM_PADDING;
// Scrolling support
item_list_t::iterator first_visible_item_iter;
@@ -2035,13 +2090,15 @@ void LLMenuGL::arrange( void )
}
else
{
+ BOOST_FOREACH(LLMenuItemGL* itemp, mItems)
+ {
+ // do first so LLMenuGLItemCall can call on_visible to determine if visible
+ itemp->buildDrawLabel();
+ }
item_list_t::iterator item_iter;
for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
{
- // do first so LLMenuGLItemCall can call on_visible to determine if visible
- (*item_iter)->buildDrawLabel();
-
if ((*item_iter)->getVisible())
{
if (!getTornOff()
@@ -2049,34 +2106,43 @@ void LLMenuGL::arrange( void )
&& *item_iter != mSpilloverBranch
&& height + (*item_iter)->getNominalHeight() > max_height - spillover_item_height)
{
- // no room for any more items
- createSpilloverBranch();
-
- std::vector<LLMenuItemGL*> items_to_remove;
- std::copy(item_iter, mItems.end(), std::back_inserter(items_to_remove));
- std::vector<LLMenuItemGL*>::iterator spillover_iter;
- for (spillover_iter= items_to_remove.begin(); spillover_iter != items_to_remove.end(); ++spillover_iter)
+ // don't show only one item
+ int visible_items = 0;
+ item_list_t::iterator count_iter;
+ for (count_iter = item_iter; count_iter != mItems.end(); ++count_iter)
{
- LLMenuItemGL* itemp = (*spillover_iter);
- removeChild(itemp);
- mSpilloverMenu->addChild(itemp);
+ if((*count_iter)->getVisible())
+ visible_items++;
}
+ if (visible_items>1)
+ {
+ // no room for any more items
+ createSpilloverBranch();
+ std::vector<LLMenuItemGL*> items_to_remove;
+ std::copy(item_iter, mItems.end(), std::back_inserter(items_to_remove));
+ std::vector<LLMenuItemGL*>::iterator spillover_iter;
+ for (spillover_iter= items_to_remove.begin(); spillover_iter != items_to_remove.end(); ++spillover_iter)
+ {
+ LLMenuItemGL* itemp = (*spillover_iter);
+ removeChild(itemp);
+ mSpilloverMenu->addChild(itemp);
+ }
- addChild(mSpilloverBranch);
- height += mSpilloverBranch->getNominalHeight();
- width = llmax( width, mSpilloverBranch->getNominalWidth() );
+ addChild(mSpilloverBranch);
- break;
- }
- else
- {
- // track our rect
- height += (*item_iter)->getNominalHeight();
- width = llmax( width, (*item_iter)->getNominalWidth() );
+ height += mSpilloverBranch->getNominalHeight();
+ width = llmax( width, mSpilloverBranch->getNominalWidth() );
+
+ break;
+ }
}
+ // track our rect
+ height += (*item_iter)->getNominalHeight();
+ width = llmax( width, (*item_iter)->getNominalWidth() );
+
if (mScrollable)
{
// Determining visible items boundaries
@@ -2300,7 +2366,9 @@ void LLMenuGL::createSpilloverBranch()
branch_params.label = label;
branch_params.branch = mSpilloverMenu;
branch_params.font.style = "italic";
-
+ branch_params.highlight_bg_color=LLUIColorTable::instance().getColor("MenuItemHighlightBgColor");
+ branch_params.highlight_fg_color=LLUIColorTable::instance().getColor("MenuItemHighlightFgColor");
+ branch_params.enabled_color=LLUIColorTable::instance().getColor("MenuItemEnabledColor");
mSpilloverBranch = LLUICtrlFactory::create<LLMenuItemBranchGL>(branch_params);
}
@@ -2442,6 +2510,56 @@ void LLMenuGL::empty( void )
deleteAllChildren();
}
+// erase group of items from menu
+void LLMenuGL::erase( S32 begin, S32 end, bool arrange/* = true*/)
+{
+ S32 items = mItems.size();
+
+ if ( items == 0 || begin >= end || begin < 0 || end > items )
+ {
+ return;
+ }
+
+ item_list_t::iterator start_position = mItems.begin();
+ std::advance(start_position, begin);
+
+ item_list_t::iterator end_position = mItems.begin();
+ std::advance(end_position, end);
+
+ for (item_list_t::iterator position_iter = start_position; position_iter != end_position; position_iter++)
+ {
+ LLUICtrl::removeChild(*position_iter);
+ }
+
+ mItems.erase(start_position, end_position);
+
+ if (arrange)
+ {
+ needsArrange();
+ }
+}
+
+// add new item at position
+void LLMenuGL::insert( S32 position, LLView * ctrl, bool arrange /*= true*/ )
+{
+ LLMenuItemGL * item = dynamic_cast<LLMenuItemGL *>(ctrl);
+
+ if (NULL == item || position < 0 || position >= mItems.size())
+ {
+ return;
+ }
+
+ item_list_t::iterator position_iter = mItems.begin();
+ std::advance(position_iter, position);
+ mItems.insert(position_iter, item);
+ LLUICtrl::addChild(item);
+
+ if (arrange)
+ {
+ needsArrange();
+ }
+}
+
// Adjust rectangle of the menu
void LLMenuGL::setLeftAndBottom(S32 left, S32 bottom)
{
@@ -2483,7 +2601,8 @@ BOOL LLMenuGL::append( LLMenuItemGL* item )
// add a separator to this menu
BOOL LLMenuGL::addSeparator()
{
- LLMenuItemGL* separator = new LLMenuItemSeparatorGL();
+ LLMenuItemSeparatorGL::Params p;
+ LLMenuItemGL* separator = LLUICtrlFactory::create<LLMenuItemSeparatorGL>(p);
return addChild(separator);
}
@@ -2492,8 +2611,8 @@ BOOL LLMenuGL::appendMenu( LLMenuGL* menu )
{
if( menu == this )
{
- llerrs << "** Attempt to attach menu to itself. This is certainly "
- << "a logic error." << llendl;
+ LL_ERRS() << "** Attempt to attach menu to itself. This is certainly "
+ << "a logic error." << LL_ENDL;
}
BOOL success = TRUE;
@@ -2516,6 +2635,30 @@ BOOL LLMenuGL::appendMenu( LLMenuGL* menu )
return success;
}
+// add a context menu branch
+BOOL LLMenuGL::appendContextSubMenu(LLMenuGL *menu)
+{
+ if (menu == this)
+ {
+ LL_ERRS() << "Can't attach a context menu to itself" << LL_ENDL;
+ }
+
+ LLContextMenuBranch *item;
+ LLContextMenuBranch::Params p;
+ p.name = menu->getName();
+ p.label = menu->getLabel();
+ p.branch = (LLContextMenu *)menu;
+ p.enabled_color=LLUIColorTable::instance().getColor("MenuItemEnabledColor");
+ p.disabled_color=LLUIColorTable::instance().getColor("MenuItemDisabledColor");
+ p.highlight_bg_color=LLUIColorTable::instance().getColor("MenuItemHighlightBgColor");
+ p.highlight_fg_color=LLUIColorTable::instance().getColor("MenuItemHighlightFgColor");
+
+ item = LLUICtrlFactory::create<LLContextMenuBranch>(p);
+ LLMenuGL::sMenuContainer->addChild(item->getBranch());
+
+ return append( item );
+}
+
void LLMenuGL::setEnabledSubMenus(BOOL enable)
{
setEnabled(enable);
@@ -2620,7 +2763,11 @@ LLMenuItemGL* LLMenuGL::highlightNextItem(LLMenuItemGL* cur_item, BOOL skip_disa
// same as giving focus to it
if (!cur_item && getTornOff())
{
- ((LLFloater*)getParent())->setFocus(TRUE);
+ LLFloater * parent = dynamic_cast<LLFloater *>(getParent());
+ if (parent)
+ {
+ parent->setFocus(TRUE);
+ }
}
// Current item position in the items list
@@ -2722,7 +2869,11 @@ LLMenuItemGL* LLMenuGL::highlightPrevItem(LLMenuItemGL* cur_item, BOOL skip_disa
// same as giving focus to it
if (!cur_item && getTornOff())
{
- ((LLFloater*)getParent())->setFocus(TRUE);
+ LLFloater * parent = dynamic_cast<LLFloater *>(getParent());
+ if (parent)
+ {
+ parent->setFocus(TRUE);
+ }
}
// Current item reverse position from the end of the list
@@ -2868,8 +3019,8 @@ BOOL LLMenuGL::handleHover( S32 x, S32 y, MASK mask )
LLVector2 mouse_avg_dir((F32)mMouseVelX, (F32)mMouseVelY);
mouse_avg_dir.normVec();
F32 interp = 0.5f * (llclamp(mouse_dir * mouse_avg_dir, 0.f, 1.f));
- mMouseVelX = llround(lerp((F32)mouse_delta_x, (F32)mMouseVelX, interp));
- mMouseVelY = llround(lerp((F32)mouse_delta_y, (F32)mMouseVelY, interp));
+ mMouseVelX = ll_round(lerp((F32)mouse_delta_x, (F32)mMouseVelX, interp));
+ mMouseVelY = ll_round(lerp((F32)mouse_delta_y, (F32)mMouseVelY, interp));
mLastMouseX = x;
mLastMouseY = y;
@@ -3020,7 +3171,7 @@ LLMenuGL* LLMenuGL::findChildMenuByName(const std::string& name, BOOL recurse) c
return menup;
}
}
- llwarns << "Child Menu " << name << " not found in menu " << getName() << llendl;
+ LL_WARNS() << "Child Menu " << name << " not found in menu " << getName() << LL_ENDL;
return NULL;
}
@@ -3052,11 +3203,29 @@ void LLMenuGL::showPopup(LLView* spawning_view, LLMenuGL* menu, S32 x, S32 y)
const S32 CURSOR_HEIGHT = 22; // Approximate "normal" cursor size
const S32 CURSOR_WIDTH = 12;
- if(menu->getChildList()->empty())
+ if (menu->getChildList()->empty())
{
return;
}
+ menu->setVisible( TRUE );
+
+ //Do not show menu if all menu items are disabled
+ BOOL item_enabled = false;
+ for (LLView::child_list_t::const_iterator itor = menu->getChildList()->begin();
+ itor != menu->getChildList()->end();
+ ++itor)
+ {
+ LLView *menu_item = (*itor);
+ item_enabled = item_enabled || menu_item->getEnabled();
+ }
+
+ if(!item_enabled)
+ {
+ menu->setVisible( FALSE );
+ return;
+ }
+
// 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.
@@ -3064,13 +3233,11 @@ void LLMenuGL::showPopup(LLView* spawning_view, LLMenuGL* menu, S32 x, S32 y)
S32 mouse_x, mouse_y;
// Resetting scrolling position
- if (menu->isScrollable())
+ if (menu->isScrollable() && menu->isScrollPositionOnShowReset())
{
menu->mFirstVisibleItem = NULL;
}
- menu->setVisible( TRUE );
-
// Fix menu rect if needed.
menu->needsArrange();
menu->arrangeAndClear();
@@ -3097,11 +3264,13 @@ void LLMenuGL::showPopup(LLView* spawning_view, LLMenuGL* menu, S32 x, S32 y)
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->translateIntoRectWithExclusion( menu_region_rect, mouse_rect );
+ if (menu->getRect().mTop > menu_region_rect.mTop)
+ {
+ // not enough space: align with top, ignore exclusion
+ menu->translateIntoRect( menu_region_rect );
+ }
menu->getParent()->sendChildToFront(menu);
-
-
-
}
///============================================================================
@@ -3318,8 +3487,8 @@ BOOL LLMenuBarGL::appendMenu( LLMenuGL* menu )
{
if( menu == this )
{
- llerrs << "** Attempt to attach menu to itself. This is certainly "
- << "a logic error." << llendl;
+ LL_ERRS() << "** Attempt to attach menu to itself. This is certainly "
+ << "a logic error." << LL_ENDL;
}
BOOL success = TRUE;
@@ -3443,7 +3612,7 @@ void LLMenuHolderGL::draw()
LLUI::pushMatrix();
{
- LLUI::translate((F32)item_rect.mLeft, (F32)item_rect.mBottom, 0.f);
+ LLUI::translate((F32)item_rect.mLeft, (F32)item_rect.mBottom);
selecteditem->getMenu()->drawBackground(selecteditem, interpolant);
selecteditem->draw();
}
@@ -3520,7 +3689,7 @@ BOOL LLMenuHolderGL::handleKey(KEY key, MASK mask, BOOL called_from_parent)
{
handled = pMenu->handleKey(key, mask, TRUE);
}
- else
+ else if (mask == MASK_NONE || (key >= KEY_LEFT && key <= KEY_DOWN))
{
//highlight first enabled one
if(pMenu->highlightNextItem(NULL))
@@ -3648,7 +3817,7 @@ void LLTearOffMenu::draw()
if (getRect().getHeight() != mTargetHeight)
{
// animate towards target height
- reshape(getRect().getWidth(), llceil(lerp((F32)getRect().getHeight(), mTargetHeight, LLCriticalDamp::getInterpolant(0.05f))));
+ reshape(getRect().getWidth(), llceil(lerp((F32)getRect().getHeight(), mTargetHeight, LLSmoothInterpolation::getInterpolant(0.05f))));
}
LLFloater::draw();
}
@@ -3743,39 +3912,6 @@ void LLTearOffMenu::closeTearOff()
mMenu->setDropShadowed(TRUE);
}
-
-//-----------------------------------------------------------------------------
-// class LLContextMenuBranch
-// A branch to another context menu
-//-----------------------------------------------------------------------------
-class LLContextMenuBranch : public LLMenuItemGL
-{
-public:
- struct Params : public LLInitParam::Block<Params, LLMenuItemGL::Params>
- {
- Mandatory<LLContextMenu*> branch;
- };
-
- LLContextMenuBranch(const Params&);
-
- virtual ~LLContextMenuBranch()
- {}
-
- // called to rebuild the draw label
- virtual void buildDrawLabel( void );
-
- // onCommit() - do the primary funcationality of the menu item.
- virtual void onCommit( void );
-
- LLContextMenu* getBranch() { return mBranch.get(); }
- void setHighlight( BOOL highlight );
-
-protected:
- void showSubMenu();
-
- LLHandle<LLContextMenu> mBranch;
-};
-
LLContextMenuBranch::LLContextMenuBranch(const LLContextMenuBranch::Params& p)
: LLMenuItemGL(p),
mBranch( p.branch()->getHandle() )
@@ -3872,7 +4008,7 @@ void LLContextMenu::setVisible(BOOL visible)
}
// Takes cursor position in screen space?
-void LLContextMenu::show(S32 x, S32 y)
+void LLContextMenu::show(S32 x, S32 y, LLView* spawning_view)
{
if (getChildList()->empty())
{
@@ -3926,6 +4062,14 @@ void LLContextMenu::show(S32 x, S32 y)
setRect(rect);
arrange();
+ if (spawning_view)
+ {
+ mSpawningViewHandle = spawning_view->getHandle();
+ }
+ else
+ {
+ mSpawningViewHandle.markDead();
+ }
LLView::setVisible(TRUE);
}
@@ -4039,49 +4183,8 @@ BOOL LLContextMenu::handleRightMouseUp( S32 x, S32 y, MASK mask )
return result;
}
-void LLContextMenu::draw()
-{
- LLMenuGL::draw();
-}
-
-BOOL LLContextMenu::appendContextSubMenu(LLContextMenu *menu)
-{
-
- if (menu == this)
- {
- llerrs << "Can't attach a context menu to itself" << llendl;
- }
-
- LLContextMenuBranch *item;
- LLContextMenuBranch::Params p;
- p.name = menu->getName();
- p.label = menu->getLabel();
- p.branch = menu;
- p.enabled_color=LLUIColorTable::instance().getColor("MenuItemEnabledColor");
- p.disabled_color=LLUIColorTable::instance().getColor("MenuItemDisabledColor");
- p.highlight_bg_color=LLUIColorTable::instance().getColor("MenuItemHighlightBgColor");
- p.highlight_fg_color=LLUIColorTable::instance().getColor("MenuItemHighlightFgColor");
-
- item = LLUICtrlFactory::create<LLContextMenuBranch>(p);
- LLMenuGL::sMenuContainer->addChild(item->getBranch());
-
- return append( item );
-}
-
bool LLContextMenu::addChild(LLView* view, S32 tab_group)
{
- LLContextMenu* context = dynamic_cast<LLContextMenu*>(view);
- if (context)
- return appendContextSubMenu(context);
-
- LLMenuItemSeparatorGL* separator = dynamic_cast<LLMenuItemSeparatorGL*>(view);
- if (separator)
- return append(separator);
-
- LLMenuItemGL* item = dynamic_cast<LLMenuItemGL*>(view);
- if (item)
- return append(item);
-
- return false;
+ return addContextChild(view, tab_group);
}
diff --git a/indra/llui/llmenugl.h b/indra/llui/llmenugl.h
index 7bde8e83ec..ae9b169691 100644..100755
--- a/indra/llui/llmenugl.h
+++ b/indra/llui/llmenugl.h
@@ -82,7 +82,7 @@ protected:
friend class LLUICtrlFactory;
public:
// LLView overrides
- /*virtual*/ void handleVisibilityChange(BOOL new_visibility);
+ /*virtual*/ void onVisibilityChange(BOOL new_visibility);
/*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask);
/*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask);
/*virtual*/ BOOL handleRightMouseUp(S32 x, S32 y, MASK mask);
@@ -372,17 +372,16 @@ public:
drop_shadow("drop_shadow", true),
bg_visible("bg_visible", true),
create_jump_keys("create_jump_keys", false),
+ keep_fixed_size("keep_fixed_size", false),
bg_color("bg_color", LLUIColorTable::instance().getColor( "MenuDefaultBgColor" )),
scrollable("scrollable", false),
max_scrollable_items("max_scrollable_items", U32_MAX),
preferred_width("preferred_width", U32_MAX),
shortcut_pad("shortcut_pad")
-
{
addSynonym(bg_visible, "opaque");
addSynonym(bg_color, "color");
-
- name = "menu";
+ addSynonym(can_tear_off, "can_tear_off");
}
};
@@ -479,6 +478,12 @@ public:
// remove all items on the menu
void empty( void );
+ // erase group of items from menu
+ void erase( S32 begin, S32 end, bool arrange = true );
+
+ // add new item at position
+ void insert( S32 begin, LLView * ctrl, bool arrange = true );
+
void setItemLastSelected(LLMenuItemGL* item); // must be in menu
U32 getItemCount(); // number of menu items
LLMenuItemGL* getItem(S32 number); // 0 = first item
@@ -517,6 +522,12 @@ public:
static class LLMenuHolderGL* sMenuContainer;
+ void resetScrollPositionOnShow(bool reset_scroll_pos) { mResetScrollPositionOnShow = reset_scroll_pos; }
+ bool isScrollPositionOnShowReset() { return mResetScrollPositionOnShow; }
+
+ // add a context menu branch
+ BOOL appendContextSubMenu(LLMenuGL *menu);
+
protected:
void createSpilloverBranch();
void cleanupSpilloverBranch();
@@ -526,6 +537,10 @@ protected:
// add a menu - this will create a cascading menu
virtual BOOL appendMenu( LLMenuGL* menu );
+ // Used in LLContextMenu and in LLTogleableMenu
+ // to add an item of context menu branch
+ bool addContextChild(LLView* view, S32 tab_group);
+
// TODO: create accessor methods for these?
typedef std::list< LLMenuItemGL* > item_list_t;
item_list_t mItems;
@@ -566,6 +581,7 @@ private:
KEY mJumpKey;
BOOL mCreateJumpKeys;
S32 mShortcutPad;
+ bool mResetScrollPositionOnShow;
}; // end class LLMenuGL
@@ -621,7 +637,7 @@ public:
virtual void updateBranchParent( LLView* parentp );
// LLView Functionality
- virtual void handleVisibilityChange( BOOL curVisibilityIn );
+ virtual void onVisibilityChange( BOOL curVisibilityIn );
virtual void draw();
@@ -650,7 +666,7 @@ public:
{
Params()
{
- visible = false;
+ changeDefault(visible, false);
}
};
@@ -665,9 +681,7 @@ public:
// can't set visibility directly, must call show or hide
virtual void setVisible (BOOL visible);
- virtual void draw ();
-
- virtual void show (S32 x, S32 y);
+ virtual void show (S32 x, S32 y, LLView* spawning_view = NULL);
virtual void hide ();
virtual BOOL handleHover ( S32 x, S32 y, MASK mask );
@@ -676,16 +690,49 @@ public:
virtual bool addChild (LLView* view, S32 tab_group = 0);
- BOOL appendContextSubMenu(LLContextMenu *menu);
+ LLHandle<LLContextMenu> getHandle() { return getDerivedHandle<LLContextMenu>(); }
- LLHandle<LLContextMenu> getHandle() { mHandle.bind(this); return mHandle; }
+ LLView* getSpawningView() const { return mSpawningViewHandle.get(); }
+ void setSpawningView(LLHandle<LLView> spawning_view) { mSpawningViewHandle = spawning_view; }
protected:
BOOL mHoveredAnyItem;
LLMenuItemGL* mHoverItem;
LLRootHandle<LLContextMenu> mHandle;
+ LLHandle<LLView> mSpawningViewHandle;
};
+//-----------------------------------------------------------------------------
+// class LLContextMenuBranch
+// A branch to another context menu
+//-----------------------------------------------------------------------------
+class LLContextMenuBranch : public LLMenuItemGL
+{
+public:
+ struct Params : public LLInitParam::Block<Params, LLMenuItemGL::Params>
+ {
+ Mandatory<LLContextMenu*> branch;
+ };
+
+ LLContextMenuBranch(const Params&);
+
+ virtual ~LLContextMenuBranch()
+ {}
+
+ // called to rebuild the draw label
+ virtual void buildDrawLabel( void );
+
+ // onCommit() - do the primary funcationality of the menu item.
+ virtual void onCommit( void );
+
+ LLContextMenu* getBranch() { return mBranch.get(); }
+ void setHighlight( BOOL highlight );
+
+protected:
+ void showSubMenu();
+
+ LLHandle<LLContextMenu> mBranch;
+};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -698,16 +745,7 @@ class LLMenuBarGL : public LLMenuGL
{
public:
struct Params : public LLInitParam::Block<Params, LLMenuGL::Params>
- {
- Params()
- {
- can_tear_off = false;
- keep_fixed_size = true;
- horizontal_layout = true;
- visible = true;
- drop_shadow = false;
- }
- };
+ {};
LLMenuBarGL( const Params& p );
virtual ~LLMenuBarGL();
@@ -825,13 +863,7 @@ class LLMenuItemTearOffGL : public LLMenuItemGL
{
public:
struct Params : public LLInitParam::Block<Params, LLMenuItemGL::Params>
- {
- Params()
- {
- name = "tear off";
- label = "~~~~~~~~~~~";
- }
- };
+ {};
LLMenuItemTearOffGL( const Params& );
@@ -863,7 +895,8 @@ class view_listener_t : public boost::signals2::trackable
{
public:
virtual bool handleEvent(const LLSD& userdata) = 0;
- virtual ~view_listener_t() {}
+ view_listener_t() { sListeners.insert(this); }
+ virtual ~view_listener_t() { sListeners.erase(this); }
static void addEnable(view_listener_t* listener, const std::string& name)
{
@@ -881,6 +914,20 @@ public:
addEnable(listener, name);
addCommit(listener, name);
}
+
+ static void cleanup()
+ {
+ listener_vector_t listeners(sListeners.begin(), sListeners.end());
+ sListeners.clear();
+
+ std::for_each(listeners.begin(), listeners.end(), DeletePointer());
+ listeners.clear();
+ }
+
+private:
+ typedef std::set<view_listener_t*> listener_map_t;
+ typedef std::vector<view_listener_t*> listener_vector_t;
+ static listener_map_t sListeners;
};
#endif // LL_LLMENUGL_H
diff --git a/indra/llui/llmodaldialog.cpp b/indra/llui/llmodaldialog.cpp
index 8c2be44904..8cf88ad5eb 100644..100755
--- a/indra/llui/llmodaldialog.cpp
+++ b/indra/llui/llmodaldialog.cpp
@@ -34,7 +34,7 @@
#include "llui.h"
#include "llwindow.h"
#include "llkeyboard.h"
-
+#include "llmenugl.h"
// static
std::list<LLModalDialog*> LLModalDialog::sModalStack;
@@ -65,7 +65,7 @@ LLModalDialog::~LLModalDialog()
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;
+ LL_ERRS() << "Attempt to delete dialog while still in sModalStack!" << LL_ENDL;
}
}
@@ -126,7 +126,7 @@ void LLModalDialog::stopModal()
}
else
{
- llwarns << "LLModalDialog::stopModal not in list!" << llendl;
+ LL_WARNS() << "LLModalDialog::stopModal not in list!" << LL_ENDL;
}
}
if (!sModalStack.empty())
@@ -161,6 +161,18 @@ void LLModalDialog::setVisible( BOOL visible )
BOOL LLModalDialog::handleMouseDown(S32 x, S32 y, MASK mask)
{
+ LLView* popup_menu = LLMenuGL::sMenuContainer->getVisibleMenu();
+ if (popup_menu != NULL)
+ {
+ S32 mx, my;
+ LLUI::getMousePositionScreen(&mx, &my);
+ LLRect menu_screen_rc = popup_menu->calcScreenRect();
+ if(!menu_screen_rc.pointInRect(mx, my))
+ {
+ LLMenuGL::sMenuContainer->hideMenus();
+ }
+ }
+
if (mModal)
{
if (!LLFloater::handleMouseDown(x, y, mask))
@@ -173,6 +185,8 @@ BOOL LLModalDialog::handleMouseDown(S32 x, S32 y, MASK mask)
{
LLFloater::handleMouseDown(x, y, mask);
}
+
+
return TRUE;
}
@@ -181,8 +195,24 @@ BOOL LLModalDialog::handleHover(S32 x, S32 y, MASK mask)
if( childrenHandleHover(x, y, mask) == NULL )
{
getWindow()->setCursor(UI_CURSOR_ARROW);
- lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << llendl;
+ LL_DEBUGS("UserInput") << "hover handled by " << getName() << LL_ENDL;
}
+
+ LLView* popup_menu = LLMenuGL::sMenuContainer->getVisibleMenu();
+ if (popup_menu != NULL)
+ {
+ S32 mx, my;
+ LLUI::getMousePositionScreen(&mx, &my);
+ LLRect menu_screen_rc = popup_menu->calcScreenRect();
+ if(menu_screen_rc.pointInRect(mx, my))
+ {
+ S32 local_x = mx - popup_menu->getRect().mLeft;
+ S32 local_y = my - popup_menu->getRect().mBottom;
+ popup_menu->handleHover(local_x, local_y, mask);
+ gFocusMgr.setMouseCapture(NULL);
+ }
+ }
+
return TRUE;
}
@@ -210,6 +240,7 @@ BOOL LLModalDialog::handleDoubleClick(S32 x, S32 y, MASK mask)
BOOL LLModalDialog::handleRightMouseDown(S32 x, S32 y, MASK mask)
{
+ LLMenuGL::sMenuContainer->hideMenus();
childrenHandleRightMouseDown(x, y, mask);
return TRUE;
}
@@ -256,7 +287,7 @@ void LLModalDialog::draw()
void LLModalDialog::centerOnScreen()
{
LLVector2 window_size = LLUI::getWindowSize();
- centerWithin(LLRect(0, 0, llround(window_size.mV[VX]), llround(window_size.mV[VY])));
+ centerWithin(LLRect(0, 0, ll_round(window_size.mV[VX]), ll_round(window_size.mV[VY])));
}
@@ -300,7 +331,7 @@ void LLModalDialog::shutdownModals()
// 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
+ // of affairs is that we accept it, but then produce an LL_ERRS() popup that
// simply makes our software look unreliable.
sModalStack.clear();
}
diff --git a/indra/llui/llmodaldialog.h b/indra/llui/llmodaldialog.h
index 4e09d11d77..f81273b96a 100644..100755
--- a/indra/llui/llmodaldialog.h
+++ b/indra/llui/llmodaldialog.h
@@ -40,7 +40,7 @@ class LLModalDialog : public LLFloater
{
public:
LLModalDialog( const LLSD& key, BOOL modal = true );
- /*virtual*/ ~LLModalDialog();
+ virtual ~LLModalDialog();
/*virtual*/ BOOL postBuild();
diff --git a/indra/llui/llmultifloater.cpp b/indra/llui/llmultifloater.cpp
index f3a48835b1..d1a597511e 100644..100755
--- a/indra/llui/llmultifloater.cpp
+++ b/indra/llui/llmultifloater.cpp
@@ -41,8 +41,8 @@ LLMultiFloater::LLMultiFloater(const LLSD& key, const LLFloater::Params& params)
mTabContainer(NULL),
mTabPos(LLTabContainer::TOP),
mAutoResize(TRUE),
- mOrigMinWidth(0),
- mOrigMinHeight(0)
+ mOrigMinWidth(params.min_width),
+ mOrigMinHeight(params.min_height)
{
}
@@ -67,14 +67,13 @@ void LLMultiFloater::buildTabContainer()
}
}
-void LLMultiFloater::onOpen(const LLSD& key)
+void LLMultiFloater::onClose(bool app_quitting)
{
-// if (mTabContainer->getTabCount() <= 0)
-// {
-// // for now, don't allow multifloaters
-// // without any child floaters
-// closeFloater();
-// }
+ if(isMinimized())
+ {
+ setMinimized(FALSE);
+ }
+ LLFloater::onClose(app_quitting);
}
void LLMultiFloater::draw()
@@ -159,7 +158,7 @@ void LLMultiFloater::addFloater(LLFloater* floaterp, BOOL select_added_floater,
if (!mTabContainer)
{
- llerrs << "Tab Container used without having been initialized." << llendl;
+ LL_ERRS() << "Tab Container used without having been initialized." << LL_ENDL;
return;
}
@@ -173,7 +172,7 @@ void LLMultiFloater::addFloater(LLFloater* floaterp, BOOL select_added_floater,
else if (floaterp->getHost())
{
// floaterp is hosted by somebody else and
- // this is adding it, so remove it from it's old host
+ // this is adding it, so remove it from its old host
floaterp->getHost()->removeFloater(floaterp);
}
else if (floaterp->getParent() == gFloaterView)
@@ -188,11 +187,13 @@ void LLMultiFloater::addFloater(LLFloater* floaterp, BOOL select_added_floater,
floater_data.mHeight = floaterp->getRect().getHeight();
floater_data.mCanMinimize = floaterp->isMinimizeable();
floater_data.mCanResize = floaterp->isResizable();
+ floater_data.mSaveRect = floaterp->mSaveRect;
// remove minimize and close buttons
floaterp->setCanMinimize(FALSE);
floaterp->setCanResize(FALSE);
floaterp->setCanDrag(FALSE);
+ floaterp->mSaveRect = FALSE;
floaterp->storeRectControl();
// avoid double rendering of floater background (makes it more opaque)
floaterp->setBackgroundVisible(FALSE);
@@ -291,6 +292,7 @@ void LLMultiFloater::removeFloater(LLFloater* floaterp)
{
LLFloaterData& floater_data = found_data_it->second;
floaterp->setCanMinimize(floater_data.mCanMinimize);
+ floaterp->mSaveRect = floater_data.mSaveRect;
if (!floater_data.mCanResize)
{
// restore original size
@@ -349,7 +351,7 @@ void LLMultiFloater::setVisible(BOOL visible)
BOOL LLMultiFloater::handleKeyHere(KEY key, MASK mask)
{
- if (key == 'W' && mask == (MASK_CONTROL|MASK_SHIFT))
+ if (key == 'W' && mask == MASK_CONTROL)
{
LLFloater* floater = getActiveFloater();
// is user closeable and is system closeable
@@ -468,23 +470,12 @@ BOOL LLMultiFloater::postBuild()
void LLMultiFloater::updateResizeLimits()
{
- static LLUICachedControl<S32> tabcntr_close_btn_size ("UITabCntrCloseBtnSize", 0);
- const LLFloater::Params& default_params = LLFloater::getDefaultParams();
- S32 floater_header_size = default_params.header_height;
- S32 tabcntr_header_height = LLPANEL_BORDER_WIDTH + tabcntr_close_btn_size;
// initialize minimum size constraint to the original xml values.
S32 new_min_width = mOrigMinWidth;
S32 new_min_height = mOrigMinHeight;
- // possibly increase minimum size constraint due to children's minimums.
- for (S32 tab_idx = 0; tab_idx < mTabContainer->getTabCount(); ++tab_idx)
- {
- LLFloater* floaterp = (LLFloater*)mTabContainer->getPanelByIndex(tab_idx);
- if (floaterp)
- {
- new_min_width = llmax(new_min_width, floaterp->getMinWidth() + LLPANEL_BORDER_WIDTH * 2);
- new_min_height = llmax(new_min_height, floaterp->getMinHeight() + floater_header_size + tabcntr_header_height);
- }
- }
+
+ computeResizeLimits(new_min_width, new_min_height);
+
setResizeLimits(new_min_width, new_min_height);
S32 cur_height = getRect().getHeight();
@@ -510,3 +501,22 @@ void LLMultiFloater::updateResizeLimits()
gFloaterView->adjustToFitScreen(this, TRUE);
}
}
+
+void LLMultiFloater::computeResizeLimits(S32& new_min_width, S32& new_min_height)
+{
+ static LLUICachedControl<S32> tabcntr_close_btn_size ("UITabCntrCloseBtnSize", 0);
+ const LLFloater::Params& default_params = LLFloater::getDefaultParams();
+ S32 floater_header_size = default_params.header_height;
+ S32 tabcntr_header_height = LLPANEL_BORDER_WIDTH + tabcntr_close_btn_size;
+
+ // possibly increase minimum size constraint due to children's minimums.
+ for (S32 tab_idx = 0; tab_idx < mTabContainer->getTabCount(); ++tab_idx)
+ {
+ LLFloater* floaterp = (LLFloater*)mTabContainer->getPanelByIndex(tab_idx);
+ if (floaterp)
+ {
+ new_min_width = llmax(new_min_width, floaterp->getMinWidth() + LLPANEL_BORDER_WIDTH * 2);
+ new_min_height = llmax(new_min_height, floaterp->getMinHeight() + floater_header_size + tabcntr_header_height);
+ }
+ }
+}
diff --git a/indra/llui/llmultifloater.h b/indra/llui/llmultifloater.h
index 9fa917eca1..c106a62527 100644..100755
--- a/indra/llui/llmultifloater.h
+++ b/indra/llui/llmultifloater.h
@@ -44,9 +44,9 @@ public:
void buildTabContainer();
virtual BOOL postBuild();
- /*virtual*/ void onOpen(const LLSD& key);
- /*virtual*/ void draw();
- /*virtual*/ void setVisible(BOOL visible);
+ /*virtual*/ void onClose(bool app_quitting);
+ virtual void draw();
+ virtual void setVisible(BOOL visible);
/*virtual*/ BOOL handleKeyHere(KEY key, MASK mask);
/*virtual*/ bool addChild(LLView* view, S32 tab_group = 0);
@@ -79,10 +79,11 @@ public:
protected:
struct LLFloaterData
{
- S32 mWidth;
- S32 mHeight;
- BOOL mCanMinimize;
- BOOL mCanResize;
+ S32 mWidth;
+ S32 mHeight;
+ BOOL mCanMinimize;
+ BOOL mCanResize;
+ BOOL mSaveRect;
};
LLTabContainer* mTabContainer;
@@ -93,9 +94,9 @@ protected:
LLTabContainer::TabPosition mTabPos;
BOOL mAutoResize;
S32 mOrigMinWidth, mOrigMinHeight; // logically const but initialized late
+
+private:
+ virtual void computeResizeLimits(S32& new_min_width, S32& new_min_height);
};
#endif // LL_MULTI_FLOATER_H
-
-
-
diff --git a/indra/llui/llmultislider.cpp b/indra/llui/llmultislider.cpp
index d4e6091ee0..0aa3e17075 100644..100755
--- a/indra/llui/llmultislider.cpp
+++ b/indra/llui/llmultislider.cpp
@@ -35,6 +35,7 @@
#include "llkeyboard.h" // for the MASK constants
#include "llcontrol.h"
#include "lluictrlfactory.h"
+#include "lluiimage.h"
#include <sstream>
@@ -65,11 +66,7 @@ LLMultiSlider::Params::Params()
mouse_up_callback("mouse_up_callback"),
thumb_width("thumb_width"),
sliders("slider")
-{
- name = "multi_slider_bar";
- mouse_opaque(true);
- follows.flags(FOLLOWS_LEFT | FOLLOWS_TOP);
-}
+{}
LLMultiSlider::LLMultiSlider(const LLMultiSlider::Params& p)
: LLF32UICtrl(p),
@@ -304,7 +301,7 @@ bool LLMultiSlider::findUnusedValue(F32& initVal)
// stop if it's filled
if(initVal == mInitialValue && !firstTry) {
- llwarns << "Whoa! Too many multi slider elements to add one to" << llendl;
+ LL_WARNS() << "Whoa! Too many multi slider elements to add one to" << LL_ENDL;
return false;
}
@@ -359,12 +356,12 @@ BOOL LLMultiSlider::handleHover(S32 x, S32 y, MASK mask)
onCommit();
getWindow()->setCursor(UI_CURSOR_ARROW);
- lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (active)" << llendl;
+ LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (active)" << LL_ENDL;
}
else
{
getWindow()->setCursor(UI_CURSOR_ARROW);
- lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (inactive)" << llendl;
+ LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (inactive)" << LL_ENDL;
}
return TRUE;
}
@@ -510,7 +507,7 @@ void LLMultiSlider::draw()
mIt->second.mTop + extra_triangle_height,
mIt->second.mLeft + mIt->second.getWidth() / 2,
mIt->second.mBottom - extra_triangle_height,
- mTriangleColor.get(), TRUE);
+ mTriangleColor.get() % opacity, TRUE);
}
}
else if (!thumb_imagep)
diff --git a/indra/llui/llmultislider.h b/indra/llui/llmultislider.h
index 2b422e89c9..2b422e89c9 100644..100755
--- a/indra/llui/llmultislider.h
+++ b/indra/llui/llmultislider.h
diff --git a/indra/llui/llmultisliderctrl.cpp b/indra/llui/llmultisliderctrl.cpp
index 91e5b6b9de..c460a08afc 100644..100755
--- a/indra/llui/llmultisliderctrl.cpp
+++ b/indra/llui/llmultisliderctrl.cpp
@@ -450,7 +450,7 @@ void LLMultiSliderCtrl::setPrecision(S32 precision)
{
if (precision < 0 || precision > 10)
{
- llerrs << "LLMultiSliderCtrl::setPrecision - precision out of range" << llendl;
+ LL_ERRS() << "LLMultiSliderCtrl::setPrecision - precision out of range" << LL_ENDL;
return;
}
diff --git a/indra/llui/llmultisliderctrl.h b/indra/llui/llmultisliderctrl.h
index b6a3542376..b6a3542376 100644..100755
--- a/indra/llui/llmultisliderctrl.h
+++ b/indra/llui/llmultisliderctrl.h
diff --git a/indra/llui/llnotificationptr.h b/indra/llui/llnotificationptr.h
index acc047527f..acc047527f 100644..100755
--- a/indra/llui/llnotificationptr.h
+++ b/indra/llui/llnotificationptr.h
diff --git a/indra/llui/llnotifications.cpp b/indra/llui/llnotifications.cpp
index cc9edfcdea..de14391d1f 100644..100755
--- a/indra/llui/llnotifications.cpp
+++ b/indra/llui/llnotifications.cpp
@@ -39,13 +39,13 @@
#include "lldir.h"
#include "llsdserialize.h"
#include "lltrans.h"
-#include "llnotificationslistener.h"
#include "llstring.h"
#include "llsdparam.h"
#include "llsdutil.h"
#include <algorithm>
#include <boost/regex.hpp>
+#include <boost/foreach.hpp>
const std::string NOTIFICATION_PERSIST_VERSION = "0.93";
@@ -59,7 +59,8 @@ void NotificationPriorityValues::declareValues()
}
LLNotificationForm::FormElementBase::FormElementBase()
-: name("name")
+: name("name"),
+ enabled("enabled", true)
{}
LLNotificationForm::FormIgnore::FormIgnore()
@@ -103,39 +104,7 @@ LLNotificationForm::Params::Params()
form_elements("")
{}
-// Local channel for persistent notifications
-// Stores only persistent notifications.
-// Class users can use connectChanged() to process persistent notifications
-// (see LLNotificationStorage for example).
-class LLPersistentNotificationChannel : public LLNotificationChannel
-{
- LOG_CLASS(LLPersistentNotificationChannel);
-public:
- LLPersistentNotificationChannel() :
- LLNotificationChannel("Persistent", "Visible", &notificationFilter, LLNotificationComparators::orderByUUID())
- {
- }
-
-private:
-
- // The channel gets all persistent notifications except those that have been canceled
- static bool notificationFilter(LLNotificationPtr pNotification)
- {
- bool handle_notification = false;
-
- handle_notification = pNotification->isPersistent()
- && !pNotification->isCancelled();
- return handle_notification;
- }
-
- void onDelete(LLNotificationPtr pNotification)
- {
- // we want to keep deleted notifications in our log, otherwise some
- // notifications will be lost on exit.
- mItems.insert(pNotification);
- }
-};
bool filterIgnoredNotifications(LLNotificationPtr notification)
{
@@ -209,6 +178,14 @@ LLNotificationForm::LLNotificationForm()
{
}
+LLNotificationForm::LLNotificationForm( const LLNotificationForm& other )
+{
+ mFormData = other.mFormData;
+ mIgnore = other.mIgnore;
+ mIgnoreMsg = other.mIgnoreMsg;
+ mIgnoreSetting = other.mIgnoreSetting;
+ mInvertSetting = other.mInvertSetting;
+}
LLNotificationForm::LLNotificationForm(const std::string& name, const LLNotificationForm::Params& p)
: mIgnore(IGNORE_NO),
@@ -237,7 +214,7 @@ LLNotificationForm::LLNotificationForm(const std::string& name, const LLNotifica
}
else
{
- LLUI::sSettingGroups["ignores"]->declareBOOL(name, show_notification, "Show notification with this name", TRUE);
+ LLUI::sSettingGroups["ignores"]->declareBOOL(name, show_notification, "Show notification with this name", LLControlVariable::PERSIST_NONDFT);
mIgnoreSetting = LLUI::sSettingGroups["ignores"]->getControl(name);
}
}
@@ -245,15 +222,6 @@ LLNotificationForm::LLNotificationForm(const std::string& name, const LLNotifica
LLParamSDParser parser;
parser.writeSD(mFormData, p.form_elements);
- mFormData = mFormData[""];
- if (!mFormData.isArray())
- {
- // change existing contents to a one element array
- LLSD new_llsd_array = LLSD::emptyArray();
- new_llsd_array.append(mFormData);
- mFormData = new_llsd_array;
- }
-
for (LLSD::array_iterator it = mFormData.beginArray(), end_it = mFormData.endArray();
it != end_it;
++it)
@@ -278,7 +246,7 @@ LLNotificationForm::LLNotificationForm(const LLSD& sd)
}
else
{
- llwarns << "Invalid form data " << sd << llendl;
+ LL_WARNS() << "Invalid form data " << sd << LL_ENDL;
mFormData = LLSD::emptyArray();
}
}
@@ -300,7 +268,7 @@ LLSD LLNotificationForm::getElement(const std::string& element_name)
}
-bool LLNotificationForm::hasElement(const std::string& element_name)
+bool LLNotificationForm::hasElement(const std::string& element_name) const
{
for (LLSD::array_const_iterator it = mFormData.beginArray();
it != mFormData.endArray();
@@ -311,7 +279,48 @@ bool LLNotificationForm::hasElement(const std::string& element_name)
return false;
}
-void LLNotificationForm::addElement(const std::string& type, const std::string& name, const LLSD& value)
+void LLNotificationForm::getElements(LLSD& elements, S32 offset)
+{
+ //Finds elements that the template did not add
+ LLSD::array_const_iterator it = mFormData.beginArray() + offset;
+
+ //Keeps track of only the dynamic elements
+ for(; it != mFormData.endArray(); ++it)
+ {
+ elements.append(*it);
+ }
+}
+
+bool LLNotificationForm::getElementEnabled(const std::string& element_name) const
+{
+ for (LLSD::array_const_iterator it = mFormData.beginArray();
+ it != mFormData.endArray();
+ ++it)
+ {
+ if ((*it)["name"].asString() == element_name)
+ {
+ return (*it)["enabled"].asBoolean();
+ }
+ }
+
+ return false;
+}
+
+void LLNotificationForm::setElementEnabled(const std::string& element_name, bool enabled)
+{
+ for (LLSD::array_iterator it = mFormData.beginArray();
+ it != mFormData.endArray();
+ ++it)
+ {
+ if ((*it)["name"].asString() == element_name)
+ {
+ (*it)["enabled"] = enabled;
+ }
+ }
+}
+
+
+void LLNotificationForm::addElement(const std::string& type, const std::string& name, const LLSD& value, bool enabled)
{
LLSD element;
element["type"] = type;
@@ -319,6 +328,7 @@ void LLNotificationForm::addElement(const std::string& type, const std::string&
element["text"] = name;
element["value"] = value;
element["index"] = mFormData.size();
+ element["enabled"] = enabled;
mFormData.append(element);
}
@@ -399,6 +409,7 @@ LLNotificationTemplate::LLNotificationTemplate(const LLNotificationTemplate::Par
: mName(p.name),
mType(p.type),
mMessage(p.value),
+ mFooter(p.footer.value),
mLabel(p.label),
mIcon(p.icon),
mURL(p.url.value),
@@ -407,33 +418,32 @@ LLNotificationTemplate::LLNotificationTemplate(const LLNotificationTemplate::Par
mURLOption(p.url.option),
mURLTarget(p.url.target),
mUnique(p.unique.isProvided()),
+ mCombineBehavior(p.unique.combine),
mPriority(p.priority),
mPersist(p.persist),
- mDefaultFunctor(p.functor.isProvided() ? p.functor() : p.name())
+ mDefaultFunctor(p.functor.isProvided() ? p.functor() : p.name()),
+ mLogToChat(p.log_to_chat),
+ mLogToIM(p.log_to_im),
+ mShowToast(p.show_toast),
+ mSoundName("")
{
if (p.sound.isProvided()
&& LLUI::sSettingGroups["config"]->controlExists(p.sound))
{
- mSoundEffect = LLUUID(LLUI::sSettingGroups["config"]->getString(p.sound));
+ mSoundName = p.sound;
}
- for(LLInitParam::ParamIterator<LLNotificationTemplate::UniquenessContext>::const_iterator it = p.unique.contexts.begin(),
- end_it = p.unique.contexts.end();
- it != end_it;
- ++it)
+ BOOST_FOREACH(const LLNotificationTemplate::UniquenessContext& context, p.unique.contexts)
{
- mUniqueContext.push_back(it->value);
+ mUniqueContext.push_back(context.value);
}
- lldebugs << "notification \"" << mName << "\": tag count is " << p.tags.size() << llendl;
+ LL_DEBUGS() << "notification \"" << mName << "\": tag count is " << p.tags.size() << LL_ENDL;
- for(LLInitParam::ParamIterator<LLNotificationTemplate::Tag>::const_iterator it = p.tags.begin(),
- end_it = p.tags.end();
- it != end_it;
- ++it)
+ BOOST_FOREACH(const LLNotificationTemplate::Tag& tag, p.tags)
{
- lldebugs << " tag \"" << std::string(it->value) << "\"" << llendl;
- mTags.push_back(it->value);
+ LL_DEBUGS() << " tag \"" << std::string(tag.value) << "\"" << LL_ENDL;
+ mTags.push_back(tag.value);
}
mForm = LLNotificationFormPtr(new LLNotificationForm(p.name, p.form_ref.form));
@@ -465,18 +475,20 @@ LLNotificationVisibilityRule::LLNotificationVisibilityRule(const LLNotificationV
}
}
-LLNotification::LLNotification(const LLNotification::Params& p) :
+LLNotification::LLNotification(const LLSDParamAdapter<Params>& p) :
mTimestamp(p.time_stamp),
mSubstitutions(p.substitutions),
mPayload(p.payload),
- mExpiresAt(0),
+ mExpiresAt(p.expiry),
mTemporaryResponder(false),
mRespondedTo(false),
mPriority(p.priority),
mCancelled(false),
mIgnored(false),
mResponderObj(NULL),
- mIsReusable(false)
+ mId(p.id.isProvided() ? p.id : LLUUID::generateNewID()),
+ mOfferFromAgent(p.offer_from_agent),
+ mIsDND(p.is_dnd)
{
if (p.functor.name.isChosen())
{
@@ -499,52 +511,52 @@ LLNotification::LLNotification(const LLNotification::Params& p) :
mResponderObj = p.responder;
}
- mId.generate();
init(p.name, p.form_elements);
}
-LLNotification::LLNotification(const LLSD& sd) :
- mTemporaryResponder(false),
- mRespondedTo(false),
- mCancelled(false),
- mIgnored(false),
- mResponderObj(NULL),
- mIsReusable(false)
+LLSD LLNotification::asLLSD(bool excludeTemplateElements)
{
- mId.generate();
- mSubstitutions = sd["substitutions"];
- mPayload = sd["payload"];
- mTimestamp = sd["time"];
- mExpiresAt = sd["expiry"];
- mPriority = (ENotificationPriority)sd["priority"].asInteger();
- mResponseFunctorName = sd["responseFunctor"].asString();
- std::string templatename = sd["name"].asString();
- init(templatename, LLSD());
- // replace form with serialized version
- mForm = LLNotificationFormPtr(new LLNotificationForm(sd["form"]));
-}
-
-
-LLSD LLNotification::asLLSD()
-{
- LLSD output;
- output["id"] = mId;
- output["name"] = mTemplatep->mName;
- output["form"] = getForm()->asLLSD();
- output["substitutions"] = mSubstitutions;
- output["payload"] = mPayload;
- output["time"] = mTimestamp;
- output["expiry"] = mExpiresAt;
- output["priority"] = (S32)mPriority;
- output["responseFunctor"] = mResponseFunctorName;
- output["reusable"] = mIsReusable;
+ LLParamSDParser parser;
- if(mResponder)
- {
- output["responder"] = mResponder->asLLSD();
+ Params p;
+ p.id = mId;
+ p.name = mTemplatep->mName;
+ p.substitutions = mSubstitutions;
+ p.payload = mPayload;
+ p.time_stamp = mTimestamp;
+ p.expiry = mExpiresAt;
+ p.priority = mPriority;
+
+ LLNotificationFormPtr templateForm = mTemplatep->mForm;
+ LLSD formElements = mForm->asLLSD();
+
+ //All form elements (dynamic or not)
+ if(!excludeTemplateElements)
+ {
+ p.form_elements = formElements;
+ }
+ //Only dynamic form elements (exclude template elements)
+ else if(templateForm->getNumElements() < formElements.size())
+ {
+ LLSD dynamicElements;
+ //Offset to dynamic elements and store them
+ mForm->getElements(dynamicElements, templateForm->getNumElements());
+ p.form_elements = dynamicElements;
+ }
+
+ if(mResponder)
+ {
+ p.functor.responder_sd = mResponder->asLLSD();
+ }
+
+ if(!mResponseFunctorName.empty())
+ {
+ p.functor.name = mResponseFunctorName;
}
+ LLSD output;
+ parser.writeSD(output, p);
return output;
}
@@ -574,7 +586,6 @@ void LLNotification::updateFrom(LLNotificationPtr other)
mRespondedTo = other->mRespondedTo;
mResponse = other->mResponse;
mTemporaryResponder = other->mTemporaryResponder;
- mIsReusable = other->isReusable();
update();
}
@@ -673,7 +684,7 @@ void LLNotification::respond(const LLSD& response)
return;
}
- if (mTemporaryResponder && !isReusable())
+ if (mTemporaryResponder)
{
LLNotificationFunctorRegistry::instance().unregisterFunctor(mResponseFunctorName);
mResponseFunctorName = "";
@@ -834,7 +845,7 @@ void LLNotification::init(const std::string& template_name, const LLSD& form_ele
//mSubstitutions["_ARGS"] = get_all_arguments_as_text(mSubstitutions);
mForm = LLNotificationFormPtr(new LLNotificationForm(*mTemplatep->mForm));
- mForm->append(form_elements);
+ mForm->append(form_elements);
// apply substitution to form labels
mForm->formatElements(mSubstitutions);
@@ -876,6 +887,16 @@ std::string LLNotification::getMessage() const
return message;
}
+std::string LLNotification::getFooter() const
+{
+ if (!mTemplatep)
+ return std::string();
+
+ std::string footer = mTemplatep->mFooter;
+ LLStringUtil::format(footer, mSubstitutions);
+ return footer;
+}
+
std::string LLNotification::getLabel() const
{
std::string label = mTemplatep->mLabel;
@@ -892,6 +913,49 @@ std::string LLNotification::getURL() const
return (mTemplatep ? url : "");
}
+bool LLNotification::canLogToChat() const
+{
+ return mTemplatep->mLogToChat;
+}
+
+bool LLNotification::canLogToIM() const
+{
+ return mTemplatep->mLogToIM;
+}
+
+bool LLNotification::canShowToast() const
+{
+ return mTemplatep->mShowToast;
+}
+
+bool LLNotification::hasFormElements() const
+{
+ return mTemplatep->mForm->getNumElements() != 0;
+}
+
+void LLNotification::playSound()
+{
+ make_ui_sound(mTemplatep->mSoundName.c_str());
+}
+
+LLNotification::ECombineBehavior LLNotification::getCombineBehavior() const
+{
+ return mTemplatep->mCombineBehavior;
+}
+
+void LLNotification::updateForm( const LLNotificationFormPtr& form )
+{
+ mForm = form;
+}
+
+void LLNotification::repost()
+{
+ mRespondedTo = false;
+ LLNotifications::instance().update(shared_from_this());
+}
+
+
+
// =========================================================
// LLNotificationChannel implementation
// ---
@@ -952,7 +1016,7 @@ bool LLNotificationChannelBase::updateItem(const LLSD& payload, LLNotificationPt
std::string cmd = payload["sigtype"];
LLNotificationSet::iterator foundItem = mItems.find(pNotification);
bool wasFound = (foundItem != mItems.end());
- bool passesFilter = mFilter(pNotification);
+ bool passesFilter = mFilter ? mFilter(pNotification) : true;
// first, we offer the result of the filter test to the simple
// signals for pass/fail. One of these is guaranteed to be called.
@@ -961,10 +1025,12 @@ bool LLNotificationChannelBase::updateItem(const LLSD& payload, LLNotificationPt
bool abortProcessing = false;
if (passesFilter)
{
+ onFilterPass(pNotification);
abortProcessing = mPassedFilter(payload);
}
else
{
+ onFilterFail(pNotification);
abortProcessing = mFailedFilter(payload);
}
@@ -982,8 +1048,8 @@ bool LLNotificationChannelBase::updateItem(const LLSD& payload, LLNotificationPt
{
// not in our list, add it and say so
mItems.insert(pNotification);
- abortProcessing = mChanged(payload);
onLoad(pNotification);
+ abortProcessing = mChanged(payload);
}
}
else if (cmd == "change")
@@ -998,18 +1064,18 @@ bool LLNotificationChannelBase::updateItem(const LLSD& payload, LLNotificationPt
{
// it already existed, so this is a change
// since it changed in place, all we have to do is resend the signal
- abortProcessing = mChanged(payload);
onChange(pNotification);
+ abortProcessing = mChanged(payload);
}
else
{
// not in our list, add it and say so
mItems.insert(pNotification);
+ onChange(pNotification);
// our payload is const, so make a copy before changing it
LLSD newpayload = payload;
newpayload["sigtype"] = "add";
abortProcessing = mChanged(newpayload);
- onChange(pNotification);
}
}
else
@@ -1018,11 +1084,11 @@ bool LLNotificationChannelBase::updateItem(const LLSD& payload, LLNotificationPt
{
// it already existed, so this is a delete
mItems.erase(pNotification);
+ onChange(pNotification);
// our payload is const, so make a copy before changing it
LLSD newpayload = payload;
newpayload["sigtype"] = "delete";
abortProcessing = mChanged(newpayload);
- onChange(pNotification);
}
// didn't pass, not on our list, do nothing
}
@@ -1036,8 +1102,8 @@ bool LLNotificationChannelBase::updateItem(const LLSD& payload, LLNotificationPt
{
// not in our list, add it and say so
mItems.insert(pNotification);
- abortProcessing = mChanged(payload);
onAdd(pNotification);
+ abortProcessing = mChanged(payload);
}
}
else if (cmd == "delete")
@@ -1045,65 +1111,35 @@ bool LLNotificationChannelBase::updateItem(const LLSD& payload, LLNotificationPt
// if we have it in our list, pass on the delete, then delete it, else do nothing
if (wasFound)
{
+ onDelete(pNotification);
abortProcessing = mChanged(payload);
- // do not delete the notification to make LLChatHistory::appendMessage add notification panel to IM window
- if( ! pNotification->isReusable() )
- {
- mItems.erase(pNotification);
- onDelete(pNotification);
- }
+ mItems.erase(pNotification);
}
}
return abortProcessing;
}
-/* static */
-LLNotificationChannelPtr LLNotificationChannel::buildChannel(const std::string& name,
- const std::string& parent,
- LLNotificationFilter filter,
- LLNotificationComparator comparator)
+LLNotificationChannel::LLNotificationChannel(const Params& p)
+: LLNotificationChannelBase(p.filter()),
+ LLInstanceTracker<LLNotificationChannel, std::string>(p.name.isProvided() ? p.name : LLUUID::generateNewID().asString()),
+ mName(p.name.isProvided() ? p.name : LLUUID::generateNewID().asString())
{
- // note: this is not a leak; notifications are self-registering.
- // This factory helps to prevent excess deletions by making sure all smart
- // pointers to notification channels come from the same source
- new LLNotificationChannel(name, parent, filter, comparator);
- return LLNotifications::instance().getChannel(name);
+ BOOST_FOREACH(const std::string& source, p.sources)
+ {
+ connectToChannel(source);
+ }
}
LLNotificationChannel::LLNotificationChannel(const std::string& name,
const std::string& parent,
- LLNotificationFilter filter,
- LLNotificationComparator comparator) :
-LLNotificationChannelBase(filter, comparator),
-mName(name),
-mParent(parent)
-{
- // store myself in the channel map
- LLNotifications::instance().addChannel(LLNotificationChannelPtr(this));
+ LLNotificationFilter filter)
+: LLNotificationChannelBase(filter),
+ LLInstanceTracker<LLNotificationChannel, std::string>(name),
+ mName(name)
+{
// bind to notification broadcast
- if (parent.empty())
- {
- LLNotifications::instance().connectChanged(
- boost::bind(&LLNotificationChannelBase::updateItem, this, _1));
- }
- else
- {
- LLNotificationChannelPtr p = LLNotifications::instance().getChannel(parent);
- p->connectChanged(boost::bind(&LLNotificationChannelBase::updateItem, this, _1));
- }
-}
-
-
-void LLNotificationChannel::setComparator(LLNotificationComparator comparator)
-{
- mComparator = comparator;
- LLNotificationSet s2(mComparator);
- s2.insert(mItems.begin(), mItems.end());
- mItems.swap(s2);
-
- // notify clients that we've been resorted
- mChanged(LLSD().with("sigtype", "sort"));
+ connectToChannel(parent);
}
bool LLNotificationChannel::isEmpty() const
@@ -1111,6 +1147,11 @@ bool LLNotificationChannel::isEmpty() const
return mItems.empty();
}
+S32 LLNotificationChannel::size() const
+{
+ return mItems.size();
+}
+
LLNotificationChannel::Iterator LLNotificationChannel::begin()
{
return mItems.begin();
@@ -1121,6 +1162,11 @@ LLNotificationChannel::Iterator LLNotificationChannel::end()
return mItems.end();
}
+size_t LLNotificationChannel::size()
+{
+ return mItems.size();
+}
+
std::string LLNotificationChannel::summarize()
{
std::string s("Channel '");
@@ -1134,24 +1180,44 @@ std::string LLNotificationChannel::summarize()
return s;
}
+void LLNotificationChannel::connectToChannel( const std::string& channel_name )
+{
+ if (channel_name.empty())
+ {
+ LLNotifications::instance().connectChanged(
+ boost::bind(&LLNotificationChannelBase::updateItem, this, _1));
+ }
+ else
+ {
+ mParents.push_back(channel_name);
+ LLNotificationChannelPtr p = LLNotifications::instance().getChannel(channel_name);
+ p->connectChanged(boost::bind(&LLNotificationChannelBase::updateItem, this, _1));
+ }
+}
// ---
// END OF LLNotificationChannel implementation
// =========================================================
-// =========================================================
+// ============================================== ===========
// LLNotifications implementation
// ---
-LLNotifications::LLNotifications() : LLNotificationChannelBase(LLNotificationFilters::includeEverything,
- LLNotificationComparators::orderByUUID()),
- mIgnoreAllNotifications(false)
+LLNotifications::LLNotifications()
+: LLNotificationChannelBase(LLNotificationFilters::includeEverything),
+ mIgnoreAllNotifications(false)
{
+ mListener.reset(new LLNotificationsListener(*this));
LLUICtrl::CommitCallbackRegistry::currentRegistrar().add("Notification.Show", boost::bind(&LLNotifications::addFromCallback, this, _2));
- mListener.reset(new LLNotificationsListener(*this));
+ // touch the instance tracker for notification channels, so that it will still be around in our destructor
+ LLInstanceTracker<LLNotificationChannel, std::string>::instanceCount();
}
+void LLNotifications::clear()
+{
+ mDefaultChannels.clear();
+}
// The expiration channel gets all notifications that are cancelled
bool LLNotifications::expirationFilter(LLNotificationPtr pNotification)
@@ -1186,7 +1252,15 @@ bool LLNotifications::uniqueFilter(LLNotificationPtr pNotif)
if (pNotif != existing_notification
&& pNotif->isEquivalentTo(existing_notification))
{
- return false;
+ if (pNotif->getCombineBehavior() == LLNotification::CANCEL_OLD)
+ {
+ cancel(existing_notification);
+ return true;
+ }
+ else
+ {
+ return false;
+ }
}
}
@@ -1195,16 +1269,18 @@ bool LLNotifications::uniqueFilter(LLNotificationPtr pNotif)
bool LLNotifications::uniqueHandler(const LLSD& payload)
{
+ std::string cmd = payload["sigtype"];
+
LLNotificationPtr pNotif = LLNotifications::instance().find(payload["id"].asUUID());
if (pNotif && pNotif->hasUniquenessConstraints())
{
- if (payload["sigtype"].asString() == "add")
+ if (cmd == "add")
{
// not a duplicate according to uniqueness criteria, so we keep it
// and store it for future uniqueness checks
mUniqueNotifications.insert(std::make_pair(pNotif->getName(), pNotif));
}
- else if (payload["sigtype"].asString() == "delete")
+ else if (cmd == "delete")
{
mUniqueNotifications.erase(pNotif->getName());
}
@@ -1217,46 +1293,50 @@ bool LLNotifications::failedUniquenessTest(const LLSD& payload)
{
LLNotificationPtr pNotif = LLNotifications::instance().find(payload["id"].asUUID());
- if (!pNotif || !pNotif->hasUniquenessConstraints())
+ std::string cmd = payload["sigtype"];
+
+ if (!pNotif || cmd != "add")
{
return false;
}
- // checks against existing unique notifications
- for (LLNotificationMap::iterator existing_it = mUniqueNotifications.find(pNotif->getName());
- existing_it != mUniqueNotifications.end();
- ++existing_it)
+ switch(pNotif->getCombineBehavior())
{
- LLNotificationPtr existing_notification = existing_it->second;
- if (pNotif != existing_notification
- && pNotif->isEquivalentTo(existing_notification))
+ case LLNotification::REPLACE_WITH_NEW:
+ // Update the existing unique notification with the data from this particular instance...
+ // This guarantees that duplicate notifications will be collapsed to the one
+ // most recently triggered
+ for (LLNotificationMap::iterator existing_it = mUniqueNotifications.find(pNotif->getName());
+ existing_it != mUniqueNotifications.end();
+ ++existing_it)
{
- // copy notification instance data over to oldest instance
- // of this unique notification and update it
- existing_notification->updateFrom(pNotif);
- // then delete the new one
- pNotif->cancel();
+ LLNotificationPtr existing_notification = existing_it->second;
+ if (pNotif != existing_notification
+ && pNotif->isEquivalentTo(existing_notification))
+ {
+ // copy notification instance data over to oldest instance
+ // of this unique notification and update it
+ existing_notification->updateFrom(pNotif);
+ // then delete the new one
+ cancel(pNotif);
+ }
}
+ break;
+ case LLNotification::KEEP_OLD:
+ break;
+ case LLNotification::CANCEL_OLD:
+ // already handled by filter logic
+ break;
+ default:
+ break;
}
return false;
}
-
-void LLNotifications::addChannel(LLNotificationChannelPtr pChan)
-{
- mChannels[pChan->getName()] = pChan;
-}
-
LLNotificationChannelPtr LLNotifications::getChannel(const std::string& channelName)
{
- ChannelMap::iterator p = mChannels.find(channelName);
- if(p == mChannels.end())
- {
- llerrs << "Did not find channel named " << channelName << llendl;
- return LLNotificationChannelPtr();
- }
- return p->second;
+ return LLNotificationChannelPtr(LLNotificationChannel::getInstance(channelName));
}
@@ -1272,24 +1352,21 @@ void LLNotifications::createDefaultChannels()
{
// now construct the various channels AFTER loading the notifications,
// because the history channel is going to rewrite the stored notifications file
- LLNotificationChannel::buildChannel("Enabled", "",
- !boost::bind(&LLNotifications::getIgnoreAllNotifications, this));
- LLNotificationChannel::buildChannel("Expiration", "Enabled",
- boost::bind(&LLNotifications::expirationFilter, this, _1));
- LLNotificationChannel::buildChannel("Unexpired", "Enabled",
- !boost::bind(&LLNotifications::expirationFilter, this, _1)); // use negated bind
- LLNotificationChannel::buildChannel("Unique", "Unexpired",
- boost::bind(&LLNotifications::uniqueFilter, this, _1));
- LLNotificationChannel::buildChannel("Ignore", "Unique",
- filterIgnoredNotifications);
- LLNotificationChannel::buildChannel("VisibilityRules", "Ignore",
- boost::bind(&LLNotifications::isVisibleByRules, this, _1));
- LLNotificationChannel::buildChannel("Visible", "VisibilityRules",
- &LLNotificationFilters::includeEverything);
-
- // create special persistent notification channel
- // this isn't a leak, don't worry about the empty "new"
- new LLPersistentNotificationChannel();
+ mDefaultChannels.push_back(new LLNotificationChannel("Enabled", "",
+ !boost::bind(&LLNotifications::getIgnoreAllNotifications, this)));
+ mDefaultChannels.push_back(new LLNotificationChannel("Expiration", "Enabled",
+ boost::bind(&LLNotifications::expirationFilter, this, _1)));
+ mDefaultChannels.push_back(new LLNotificationChannel("Unexpired", "Enabled",
+ !boost::bind(&LLNotifications::expirationFilter, this, _1))); // use negated bind
+ mDefaultChannels.push_back(new LLNotificationChannel("Unique", "Unexpired",
+ boost::bind(&LLNotifications::uniqueFilter, this, _1)));
+ mDefaultChannels.push_back(new LLNotificationChannel("Ignore", "Unique",
+ filterIgnoredNotifications));
+ mDefaultChannels.push_back(new LLNotificationChannel("VisibilityRules", "Ignore",
+ boost::bind(&LLNotifications::isVisibleByRules, this, _1)));
+ mDefaultChannels.push_back(new LLNotificationChannel("Visible", "VisibilityRules",
+ &LLNotificationFilters::includeEverything));
+ mDefaultChannels.push_back(new LLPersistentNotificationChannel());
// connect action methods to these channels
LLNotifications::instance().getChannel("Enabled")->
@@ -1300,26 +1377,14 @@ void LLNotifications::createDefaultChannels()
// usage LLStopWhenHandled combiner in LLStandardSignal
LLNotifications::instance().getChannel("Unique")->
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("Unique")->
+ connectFailedFilter(boost::bind(&LLNotifications::failedUniquenessTest, this, _1));
LLNotifications::instance().getChannel("Ignore")->
connectFailedFilter(&handleIgnoredNotification);
LLNotifications::instance().getChannel("VisibilityRules")->
connectFailedFilter(&visibilityRuleMached);
}
-bool LLNotifications::addTemplate(const std::string &name,
- LLNotificationTemplatePtr theTemplate)
-{
- if (mTemplates.count(name))
- {
- llwarns << "LLNotifications -- attempted to add template '" << name << "' twice." << llendl;
- return false;
- }
- mTemplates[name] = theTemplate;
- return true;
-}
LLNotificationTemplatePtr LLNotifications::getTemplate(const std::string& name)
{
@@ -1346,7 +1411,7 @@ void LLNotifications::forceResponse(const LLNotification::Params& params, S32 op
if (selected_item.isUndefined())
{
- llwarns << "Invalid option" << option << " for notification " << (std::string)params.name << llendl;
+ LL_WARNS() << "Invalid option" << option << " for notification " << (std::string)params.name << LL_ENDL;
return;
}
response[selected_item["name"].asString()] = true;
@@ -1380,12 +1445,12 @@ void replaceSubstitutionStrings(LLXMLNodePtr node, StringMap& replacements)
if (found != replacements.end())
{
replacement = found->second;
- lldebugs << "replaceSubstitutionStrings: value: \"" << value << "\" repl: \"" << replacement << "\"." << llendl;
+ LL_DEBUGS() << "replaceSubstitutionStrings: value: \"" << value << "\" repl: \"" << replacement << "\"." << LL_ENDL;
it->second->setValue(replacement);
}
else
{
- llwarns << "replaceSubstitutionStrings FAILURE: could not find replacement \"" << value << "\"." << llendl;
+ LL_WARNS() << "replaceSubstitutionStrings FAILURE: could not find replacement \"" << value << "\"." << LL_ENDL;
}
}
}
@@ -1404,95 +1469,108 @@ void replaceFormText(LLNotificationForm::Params& form, const std::string& patter
{
form.ignore.text = replace;
}
- for (LLInitParam::ParamIterator<LLNotificationForm::FormElement>::iterator it = form.form_elements.elements.begin(),
- end_it = form.form_elements.elements.end();
- it != end_it;
- ++it)
+
+ BOOST_FOREACH(LLNotificationForm::FormElement& element, form.form_elements.elements)
{
- if (it->button.isChosen() && it->button.text() == pattern)
+ if (element.button.isChosen() && element.button.text() == pattern)
{
- it->button.text = replace;
+ element.button.text = replace;
}
}
}
+void addPathIfExists(const std::string& new_path, std::vector<std::string>& paths)
+{
+ if (gDirUtilp->fileExists(new_path))
+ {
+ paths.push_back(new_path);
+ }
+}
+
bool LLNotifications::loadTemplates()
{
- const std::string xml_filename = "notifications.xml";
- std::string full_filename = gDirUtilp->findSkinnedFilename(LLUI::getXUIPaths().front(), xml_filename);
+ LL_INFOS() << "Reading notifications template" << LL_ENDL;
+ // Passing findSkinnedFilenames(constraint=LLDir::ALL_SKINS) makes it
+ // output all relevant pathnames instead of just the ones from the most
+ // specific skin.
+ std::vector<std::string> search_paths =
+ gDirUtilp->findSkinnedFilenames(LLDir::XUI, "notifications.xml", LLDir::ALL_SKINS);
+ std::string base_filename = search_paths.front();
LLXMLNodePtr root;
- BOOL success = LLUICtrlFactory::getLayeredXMLNode(xml_filename, root);
-
+ BOOL success = LLXMLNode::getLayeredXMLNode(root, search_paths);
+
if (!success || root.isNull() || !root->hasName( "notifications" ))
{
- llerrs << "Problem reading UI Notifications file: " << full_filename << llendl;
+ LL_ERRS() << "Problem reading XML from UI Notifications file: " << base_filename << LL_ENDL;
return false;
}
LLNotificationTemplate::Notifications params;
LLXUIParser parser;
- parser.readXUI(root, params, full_filename);
+ parser.readXUI(root, params, base_filename);
if(!params.validateBlock())
{
- llerrs << "Problem reading UI Notifications file: " << full_filename << llendl;
+ LL_ERRS() << "Problem reading XUI from UI Notifications file: " << base_filename << LL_ENDL;
return false;
}
mTemplates.clear();
- for(LLInitParam::ParamIterator<LLNotificationTemplate::GlobalString>::const_iterator it = params.strings.begin(), end_it = params.strings.end();
- it != end_it;
- ++it)
+ BOOST_FOREACH(LLNotificationTemplate::GlobalString& string, params.strings)
{
- mGlobalStrings[it->name] = it->value;
+ mGlobalStrings[string.name] = string.value;
}
std::map<std::string, LLNotificationForm::Params> form_templates;
- for(LLInitParam::ParamIterator<LLNotificationTemplate::Template>::const_iterator it = params.templates.begin(), end_it = params.templates.end();
- it != end_it;
- ++it)
+ BOOST_FOREACH(LLNotificationTemplate::Template& notification_template, params.templates)
{
- form_templates[it->name] = it->form;
+ form_templates[notification_template.name] = notification_template.form;
}
- for(LLInitParam::ParamIterator<LLNotificationTemplate::Params>::iterator it = params.notifications.begin(), end_it = params.notifications.end();
- it != end_it;
- ++it)
+ BOOST_FOREACH(LLNotificationTemplate::Params& notification, params.notifications)
{
- if (it->form_ref.form_template.isChosen())
+ if (notification.form_ref.form_template.isChosen())
{
// replace form contents from template
- it->form_ref.form = form_templates[it->form_ref.form_template.name];
- if(it->form_ref.form_template.yes_text.isProvided())
+ notification.form_ref.form = form_templates[notification.form_ref.form_template.name];
+ if(notification.form_ref.form_template.yes_text.isProvided())
{
- replaceFormText(it->form_ref.form, "$yestext", it->form_ref.form_template.yes_text);
+ replaceFormText(notification.form_ref.form, "$yestext", notification.form_ref.form_template.yes_text);
}
- if(it->form_ref.form_template.no_text.isProvided())
+ if(notification.form_ref.form_template.no_text.isProvided())
{
- replaceFormText(it->form_ref.form, "$notext", it->form_ref.form_template.no_text);
+ replaceFormText(notification.form_ref.form, "$notext", notification.form_ref.form_template.no_text);
}
- if(it->form_ref.form_template.cancel_text.isProvided())
+ if(notification.form_ref.form_template.cancel_text.isProvided())
{
- replaceFormText(it->form_ref.form, "$canceltext", it->form_ref.form_template.cancel_text);
+ replaceFormText(notification.form_ref.form, "$canceltext", notification.form_ref.form_template.cancel_text);
}
- if(it->form_ref.form_template.ignore_text.isProvided())
+ if(notification.form_ref.form_template.help_text.isProvided())
{
- replaceFormText(it->form_ref.form, "$ignoretext", it->form_ref.form_template.ignore_text);
+ replaceFormText(notification.form_ref.form, "$helptext", notification.form_ref.form_template.help_text);
+ }
+ if(notification.form_ref.form_template.ignore_text.isProvided())
+ {
+ replaceFormText(notification.form_ref.form, "$ignoretext", notification.form_ref.form_template.ignore_text);
}
}
- addTemplate(it->name, LLNotificationTemplatePtr(new LLNotificationTemplate(*it)));
+ mTemplates[notification.name] = LLNotificationTemplatePtr(new LLNotificationTemplate(notification));
}
+ LL_INFOS() << "...done" << LL_ENDL;
+
return true;
}
bool LLNotifications::loadVisibilityRules()
{
const std::string xml_filename = "notification_visibility.xml";
- std::string full_filename = gDirUtilp->findSkinnedFilename(LLUI::getXUIPaths().front(), xml_filename);
+ // Note that here we're looking for the "en" version, the default
+ // language, rather than the most localized version of this file.
+ std::string full_filename = gDirUtilp->findSkinnedFilenameBaseLang(LLDir::XUI, xml_filename);
LLNotificationVisibilityRule::Rules params;
LLSimpleXUIParser parser;
@@ -1500,18 +1578,15 @@ bool LLNotifications::loadVisibilityRules()
if(!params.validateBlock())
{
- llerrs << "Problem reading UI Notification Visibility Rules file: " << full_filename << llendl;
+ LL_ERRS() << "Problem reading UI Notification Visibility Rules file: " << full_filename << LL_ENDL;
return false;
}
mVisibilityRules.clear();
- for(LLInitParam::ParamIterator<LLNotificationVisibilityRule::Rule>::iterator it = params.rules.begin(),
- end_it = params.rules.end();
- it != end_it;
- ++it)
+ BOOST_FOREACH(LLNotificationVisibilityRule::Rule& rule, params.rules)
{
- mVisibilityRules.push_back(LLNotificationVisibilityRulePtr(new LLNotificationVisibilityRule(*it)));
+ mVisibilityRules.push_back(LLNotificationVisibilityRulePtr(new LLNotificationVisibilityRule(rule)));
}
return true;
@@ -1520,37 +1595,35 @@ bool LLNotifications::loadVisibilityRules()
// Add a simple notification (from XUI)
void LLNotifications::addFromCallback(const LLSD& name)
{
- add(LLNotification::Params().name(name.asString()));
+ add(name.asString(), LLSD(), LLSD());
}
-LLNotificationPtr LLNotifications::add(const std::string& name,
- const LLSD& substitutions,
- const LLSD& payload)
+LLNotificationPtr LLNotifications::add(const std::string& name, const LLSD& substitutions, const LLSD& payload)
{
LLNotification::Params::Functor functor_p;
functor_p.name = name;
return add(LLNotification::Params().name(name).substitutions(substitutions).payload(payload).functor(functor_p));
}
-LLNotificationPtr LLNotifications::add(const std::string& name,
- const LLSD& substitutions,
- const LLSD& payload,
- const std::string& functor_name)
+LLNotificationPtr LLNotifications::add(const std::string& name, const LLSD& substitutions, const LLSD& payload, const std::string& functor_name)
{
LLNotification::Params::Functor functor_p;
functor_p.name = functor_name;
- return add(LLNotification::Params().name(name).substitutions(substitutions).payload(payload).functor(functor_p));
+ return add(LLNotification::Params().name(name)
+ .substitutions(substitutions)
+ .payload(payload)
+ .functor(functor_p));
}
//virtual
-LLNotificationPtr LLNotifications::add(const std::string& name,
- const LLSD& substitutions,
- const LLSD& payload,
- LLNotificationFunctorRegistry::ResponseFunctor functor)
+LLNotificationPtr LLNotifications::add(const std::string& name, const LLSD& substitutions, const LLSD& payload, LLNotificationFunctorRegistry::ResponseFunctor functor)
{
LLNotification::Params::Functor functor_p;
functor_p.function = functor;
- return add(LLNotification::Params().name(name).substitutions(substitutions).payload(payload).functor(functor_p));
+ return add(LLNotification::Params().name(name)
+ .substitutions(substitutions)
+ .payload(payload)
+ .functor(functor_p));
}
// generalized add function that takes a parameter block object for more complex instantiations
@@ -1570,7 +1643,7 @@ void LLNotifications::add(const LLNotificationPtr pNotif)
LLNotificationSet::iterator it=mItems.find(pNotif);
if (it != mItems.end())
{
- llerrs << "Notification added a second time to the master notification channel." << llendl;
+ LL_ERRS() << "Notification added a second time to the master notification channel." << LL_ENDL;
}
updateItem(LLSD().with("sigtype", "add").with("id", pNotif->id()), pNotif);
@@ -1578,15 +1651,14 @@ void LLNotifications::add(const LLNotificationPtr pNotif)
void LLNotifications::cancel(LLNotificationPtr pNotif)
{
- if (pNotif == NULL) return;
+ if (pNotif == NULL || pNotif->isCancelled()) return;
LLNotificationSet::iterator it=mItems.find(pNotif);
- if (it == mItems.end())
+ if (it != mItems.end())
{
- llerrs << "Attempted to delete nonexistent notification " << pNotif->getName() << llendl;
+ pNotif->cancel();
+ updateItem(LLSD().with("sigtype", "delete").with("id", pNotif->id()), pNotif);
}
- pNotif->cancel();
- updateItem(LLSD().with("sigtype", "delete").with("id", pNotif->id()), pNotif);
}
void LLNotifications::cancelByName(const std::string& name)
@@ -1625,11 +1697,11 @@ void LLNotifications::update(const LLNotificationPtr pNotif)
LLNotificationPtr LLNotifications::find(LLUUID uuid)
{
- LLNotificationPtr target = LLNotificationPtr(new LLNotification(uuid));
+ LLNotificationPtr target = LLNotificationPtr(new LLNotification(LLNotification::Params().id(uuid)));
LLNotificationSet::iterator it=mItems.find(target);
if (it == mItems.end())
{
- llwarns << "Tried to dereference uuid '" << uuid << "' as a notification key but didn't find it." << llendl;
+ LL_DEBUGS("Notifications") << "Tried to dereference uuid '" << uuid << "' as a notification key but didn't find it." << LL_ENDL;
return LLNotificationPtr((LLNotification*)NULL);
}
else
@@ -1680,14 +1752,13 @@ bool LLNotifications::isVisibleByRules(LLNotificationPtr n)
for(it = mVisibilityRules.begin(); it != mVisibilityRules.end(); it++)
{
// An empty type/tag/name string will match any notification, so only do the comparison when the string is non-empty in the rule.
-
- lldebugs
+ LL_DEBUGS()
<< "notification \"" << n->getName() << "\" "
<< "testing against " << ((*it)->mVisible?"show":"hide") << " rule, "
<< "name = \"" << (*it)->mName << "\" "
<< "tag = \"" << (*it)->mTag << "\" "
<< "type = \"" << (*it)->mType << "\" "
- << llendl;
+ << LL_ENDL;
if(!(*it)->mType.empty())
{
@@ -1726,9 +1797,9 @@ bool LLNotifications::isVisibleByRules(LLNotificationPtr n)
if((*it)->mResponse.empty())
{
// Response property is empty. Cancel this notification.
- lldebugs << "cancelling notification " << n->getName() << llendl;
+ LL_DEBUGS() << "cancelling notification " << n->getName() << LL_ENDL;
- n->cancel();
+ cancel(n);
}
else
{
@@ -1737,7 +1808,7 @@ bool LLNotifications::isVisibleByRules(LLNotificationPtr n)
// TODO: verify that the response template has an item with the correct name
response[(*it)->mResponse] = true;
- lldebugs << "responding to notification " << n->getName() << " with response = " << response << llendl;
+ LL_DEBUGS() << "responding to notification " << n->getName() << " with response = " << response << LL_ENDL;
n->respond(response);
}
@@ -1749,7 +1820,7 @@ bool LLNotifications::isVisibleByRules(LLNotificationPtr n)
break;
}
- lldebugs << "allowing notification " << n->getName() << llendl;
+ LL_DEBUGS() << "allowing notification " << n->getName() << LL_ENDL;
return true;
}
@@ -1765,22 +1836,18 @@ std::ostream& operator<<(std::ostream& s, const LLNotification& notification)
return s;
}
-//static
-void LLPostponedNotification::lookupName(LLPostponedNotification* thiz,
- const LLUUID& id,
+void LLPostponedNotification::lookupName(const LLUUID& id,
bool is_group)
{
if (is_group)
{
gCacheName->getGroup(id,
boost::bind(&LLPostponedNotification::onGroupNameCache,
- thiz, _1, _2, _3));
+ this, _1, _2, _3));
}
else
{
- LLAvatarNameCache::get(id,
- boost::bind(&LLPostponedNotification::onAvatarNameCache,
- thiz, _1, _2));
+ fetchAvatarName(id);
}
}
@@ -1791,15 +1858,30 @@ void LLPostponedNotification::onGroupNameCache(const LLUUID& id,
finalizeName(full_name);
}
+void LLPostponedNotification::fetchAvatarName(const LLUUID& id)
+{
+ if (id.notNull())
+ {
+ if (mAvatarNameCacheConnection.connected())
+ {
+ mAvatarNameCacheConnection.disconnect();
+ }
+
+ mAvatarNameCacheConnection = LLAvatarNameCache::get(id, boost::bind(&LLPostponedNotification::onAvatarNameCache, this, _1, _2));
+ }
+}
+
void LLPostponedNotification::onAvatarNameCache(const LLUUID& agent_id,
const LLAvatarName& av_name)
{
+ mAvatarNameCacheConnection.disconnect();
+
std::string name = av_name.getCompleteName();
// from PE merge - we should figure out if this is the right thing to do
if (name.empty())
{
- llwarns << "Empty name received for Id: " << agent_id << llendl;
+ LL_WARNS() << "Empty name received for Id: " << agent_id << LL_ENDL;
name = SYSTEM_FROM;
}
diff --git a/indra/llui/llnotifications.h b/indra/llui/llnotifications.h
index 34d3537781..3cf432f330 100644..100755
--- a/indra/llui/llnotifications.h
+++ b/indra/llui/llnotifications.h
@@ -87,17 +87,20 @@
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/type_traits.hpp>
+#include <boost/signals2.hpp>
+#include <boost/range.hpp>
-// we want to minimize external dependencies, but this one is important
-#include "llsd.h"
-
-// and we need this to manage the notification callbacks
#include "llevents.h"
#include "llfunctorregistry.h"
-#include "llpointer.h"
#include "llinitparam.h"
-#include "llnotificationslistener.h"
+#include "llinstancetracker.h"
+#include "llmortician.h"
#include "llnotificationptr.h"
+#include "llpointer.h"
+#include "llrefcount.h"
+#include "llsdparam.h"
+
+#include "llnotificationslistener.h"
class LLAvatarName;
typedef enum e_notification_priority
@@ -139,6 +142,7 @@ typedef LLFunctorRegistration<LLNotificationResponder> LLNotificationFunctorRegi
class LLNotificationContext : public LLInstanceTracker<LLNotificationContext, LLUUID>
{
public:
+
LLNotificationContext() : LLInstanceTracker<LLNotificationContext, LLUUID>(LLUUID::generateNewID())
{
}
@@ -164,6 +168,7 @@ public:
struct FormElementBase : public LLInitParam::Block<FormElementBase>
{
Optional<std::string> name;
+ Optional<bool> enabled;
FormElementBase();
};
@@ -201,7 +206,7 @@ public:
FormInput();
};
- struct FormElement : public LLInitParam::Choice<FormElement>
+ struct FormElement : public LLInitParam::ChoiceBlock<FormElement>
{
Alternative<FormButton> button;
Alternative<FormInput> input;
@@ -233,16 +238,21 @@ public:
} EIgnoreType;
LLNotificationForm();
+ LLNotificationForm(const LLNotificationForm&);
LLNotificationForm(const LLSD& sd);
LLNotificationForm(const std::string& name, const Params& p);
+ void fromLLSD(const LLSD& sd);
LLSD asLLSD() const;
S32 getNumElements() { return mFormData.size(); }
LLSD getElement(S32 index) { return mFormData.get(index); }
LLSD getElement(const std::string& element_name);
- bool hasElement(const std::string& element_name);
- void addElement(const std::string& type, const std::string& name, const LLSD& value = LLSD());
+ void getElements(LLSD& elements, S32 offset = 0);
+ bool hasElement(const std::string& element_name) const;
+ bool getElementEnabled(const std::string& element_name) const;
+ void setElementEnabled(const std::string& element_name, bool enabled);
+ void addElement(const std::string& type, const std::string& name, const LLSD& value = LLSD(), bool enabled = true);
void formatElements(const LLSD& substitutions);
// appends form elements from another form serialized as LLSD
void append(const LLSD& sub_form);
@@ -296,42 +306,52 @@ LOG_CLASS(LLNotification);
friend class LLNotifications;
public:
+
// parameter object used to instantiate a new notification
struct Params : public LLInitParam::Block<Params>
{
friend class LLNotification;
Mandatory<std::string> name;
-
- // optional
- Optional<LLSD> substitutions;
- Optional<LLSD> payload;
+ Optional<LLUUID> id;
+ Optional<LLSD> substitutions,
+ form_elements,
+ payload;
Optional<ENotificationPriority, NotificationPriorityValues> priority;
- Optional<LLSD> form_elements;
- Optional<LLDate> time_stamp;
+ Optional<LLDate> time_stamp,
+ expiry;
Optional<LLNotificationContext*> context;
Optional<void*> responder;
+ Optional<bool> offer_from_agent;
+ Optional<bool> is_dnd;
- struct Functor : public LLInitParam::Choice<Functor>
+ struct Functor : public LLInitParam::ChoiceBlock<Functor>
{
Alternative<std::string> name;
Alternative<LLNotificationFunctorRegistry::ResponseFunctor> function;
Alternative<LLNotificationResponderPtr> responder;
+ Alternative<LLSD> responder_sd;
Functor()
- : name("functor_name"),
+ : name("responseFunctor"),
function("functor"),
- responder("responder")
+ responder("responder"),
+ responder_sd("responder_sd")
{}
};
Optional<Functor> functor;
Params()
: name("name"),
+ id("id"),
priority("priority", NOTIFICATION_PRIORITY_UNSPECIFIED),
- time_stamp("time_stamp"),
+ time_stamp("time"),
payload("payload"),
- form_elements("form_elements")
+ form_elements("form"),
+ substitutions("substitutions"),
+ expiry("expiry"),
+ offer_from_agent("offer_from_agent", false),
+ is_dnd("is_dnd", false)
{
time_stamp = LLDate::now();
responder = NULL;
@@ -340,9 +360,13 @@ public:
Params(const std::string& _name)
: name("name"),
priority("priority", NOTIFICATION_PRIORITY_UNSPECIFIED),
- time_stamp("time_stamp"),
+ time_stamp("time"),
payload("payload"),
- form_elements("form_elements")
+ form_elements("form"),
+ substitutions("substitutions"),
+ expiry("expiry"),
+ offer_from_agent("offer_from_agent", false),
+ is_dnd("is_dnd", false)
{
functor.name = _name;
name = _name;
@@ -355,7 +379,7 @@ public:
private:
- LLUUID mId;
+ const LLUUID mId;
LLSD mPayload;
LLSD mSubstitutions;
LLDate mTimestamp;
@@ -367,8 +391,9 @@ private:
ENotificationPriority mPriority;
LLNotificationFormPtr mForm;
void* mResponderObj; // TODO - refactor/remove this field
- bool mIsReusable;
LLNotificationResponderPtr mResponder;
+ bool mOfferFromAgent;
+ bool mIsDND;
// a reference to the template
LLNotificationTemplatePtr mTemplatep;
@@ -392,18 +417,10 @@ private:
void init(const std::string& template_name, const LLSD& form_elements);
- LLNotification(const Params& p);
-
- // this is just for making it easy to look things up in a set organized by UUID -- DON'T USE IT
- // for anything real!
- LLNotification(LLUUID uuid) : mId(uuid), mCancelled(false), mRespondedTo(false), mIgnored(false), mPriority(NOTIFICATION_PRIORITY_UNSPECIFIED), mTemporaryResponder(false) {}
-
void cancel();
public:
-
- // constructor from a saved notification
- LLNotification(const LLSD& sd);
+ LLNotification(const LLSDParamAdapter<Params>& p);
void setResponseFunctor(std::string const &responseFunctorName);
@@ -446,7 +463,12 @@ public:
// ["time"] = time at which notification was generated;
// ["expiry"] = time at which notification expires;
// ["responseFunctor"] = name of registered functor that handles responses to notification;
- LLSD asLLSD();
+ LLSD asLLSD(bool excludeTemplateElements = false);
+
+ const LLNotificationFormPtr getForm();
+ void updateForm(const LLNotificationFormPtr& form);
+
+ void repost();
void respond(const LLSD& sd);
void respondWithDefault();
@@ -507,14 +529,43 @@ public:
return mTimestamp;
}
+ bool getOfferFromAgent() const
+ {
+ return mOfferFromAgent;
+ }
+
+ bool isDND() const
+ {
+ return mIsDND;
+ }
+
+ void setDND(const bool flag)
+ {
+ mIsDND = flag;
+ }
+
std::string getType() const;
std::string getMessage() const;
+ std::string getFooter() const;
std::string getLabel() const;
std::string getURL() const;
S32 getURLOption() const;
S32 getURLOpenExternally() const;
+ bool canLogToChat() const;
+ bool canLogToIM() const;
+ bool canShowToast() const;
+ bool hasFormElements() const;
+ void playSound();
+
+ typedef enum e_combine_behavior
+ {
+ REPLACE_WITH_NEW,
+ KEEP_OLD,
+ CANCEL_OLD
+
+ } ECombineBehavior;
- const LLNotificationFormPtr getForm();
+ ECombineBehavior getCombineBehavior() const;
const LLDate getExpiration() const
{
@@ -531,10 +582,6 @@ public:
return mId;
}
- bool isReusable() { return mIsReusable; }
-
- void setReusable(bool reusable) { mIsReusable = reusable; }
-
// comparing two notifications normally means comparing them by UUID (so we can look them
// up quickly this way)
bool operator<(const LLNotification& rhs) const
@@ -646,51 +693,24 @@ namespace LLNotificationFilters
namespace LLNotificationComparators
{
- typedef enum e_direction { ORDER_DECREASING, ORDER_INCREASING } EDirection;
-
- // generic order functor that takes method or member variable reference
- template<typename T>
- struct orderBy
+ struct orderByUUID
{
- typedef boost::function<T (LLNotificationPtr)> field_t;
- orderBy(field_t field, EDirection direction = ORDER_INCREASING) : mField(field), mDirection(direction) {}
bool operator()(LLNotificationPtr lhs, LLNotificationPtr rhs)
{
- if (mDirection == ORDER_DECREASING)
- {
- return mField(lhs) > mField(rhs);
- }
- else
- {
- return mField(lhs) < mField(rhs);
- }
+ return lhs->id() < rhs->id();
}
-
- field_t mField;
- EDirection mDirection;
- };
-
- struct orderByUUID : public orderBy<const LLUUID&>
- {
- orderByUUID(EDirection direction = ORDER_INCREASING) : orderBy<const LLUUID&>(&LLNotification::id, direction) {}
- };
-
- struct orderByDate : public orderBy<const LLDate&>
- {
- orderByDate(EDirection direction = ORDER_INCREASING) : orderBy<const LLDate&>(&LLNotification::getDate, direction) {}
};
};
typedef boost::function<bool (LLNotificationPtr)> LLNotificationFilter;
-typedef boost::function<bool (LLNotificationPtr, LLNotificationPtr)> LLNotificationComparator;
-typedef std::set<LLNotificationPtr, LLNotificationComparator> LLNotificationSet;
+typedef std::set<LLNotificationPtr, LLNotificationComparators::orderByUUID> LLNotificationSet;
typedef std::multimap<std::string, LLNotificationPtr> LLNotificationMap;
// ========================================================
// Abstract base class (interface) for a channel; also used for the master container.
// This lets us arrange channels into a call hierarchy.
-// We maintain a heirarchy of notification channels; events are always started at the top
+// We maintain a hierarchy of notification channels; events are always started at the top
// and propagated through the hierarchy only if they pass a filter.
// Any channel can be created with a parent. A null parent (empty string) means it's
// tied to the root of the tree (the LLNotifications class itself).
@@ -704,12 +724,14 @@ typedef std::multimap<std::string, LLNotificationPtr> LLNotificationMap;
// all of the built-in tests should attach to the "Visible" channel
//
class LLNotificationChannelBase :
- public LLEventTrackable
+ public LLEventTrackable,
+ public LLRefCount
{
LOG_CLASS(LLNotificationChannelBase);
public:
- LLNotificationChannelBase(LLNotificationFilter filter, LLNotificationComparator comp) :
- mFilter(filter), mItems(comp)
+ LLNotificationChannelBase(LLNotificationFilter filter)
+ : mFilter(filter),
+ mItems()
{}
virtual ~LLNotificationChannelBase() {}
// you can also connect to a Channel, so you can be notified of
@@ -775,6 +797,9 @@ protected:
virtual void onDelete(LLNotificationPtr p) {}
virtual void onChange(LLNotificationPtr p) {}
+ virtual void onFilterPass(LLNotificationPtr p) {}
+ virtual void onFilterFail(LLNotificationPtr p) {}
+
bool updateItem(const LLSD& payload, LLNotificationPtr pNotification);
LLNotificationFilter mFilter;
};
@@ -784,63 +809,58 @@ protected:
// destroy it, but if it becomes necessary to do so, the shared_ptr model
// will ensure that we don't leak resources.
class LLNotificationChannel;
-typedef boost::shared_ptr<LLNotificationChannel> LLNotificationChannelPtr;
+typedef boost::intrusive_ptr<LLNotificationChannel> LLNotificationChannelPtr;
// manages a list of notifications
// Note that if this is ever copied around, we might find ourselves with multiple copies
// of a queue with notifications being added to different nonequivalent copies. So we
-// make it inherit from boost::noncopyable, and then create a map of shared_ptr to manage it.
-//
-// NOTE: LLNotificationChannel is self-registering. The *correct* way to create one is to
-// do something like:
-// LLNotificationChannel::buildChannel("name", "parent"...);
-// This returns an LLNotificationChannelPtr, which you can store, or
-// you can then retrieve the channel by using the registry:
-// LLNotifications::instance().getChannel("name")...
+// make it inherit from boost::noncopyable, and then create a map of LLPointer to manage it.
//
class LLNotificationChannel :
boost::noncopyable,
- public LLNotificationChannelBase
+ public LLNotificationChannelBase,
+ public LLInstanceTracker<LLNotificationChannel, std::string>
{
LOG_CLASS(LLNotificationChannel);
public:
+ // Notification Channels have a filter, which determines which notifications
+ // will be added to this channel.
+ // Channel filters cannot change.
+ struct Params : public LLInitParam::Block<Params>
+ {
+ Mandatory<std::string> name;
+ Optional<LLNotificationFilter> filter;
+ Multiple<std::string> sources;
+ };
+
+ LLNotificationChannel(const Params& p = Params());
+ LLNotificationChannel(const std::string& name, const std::string& parent, LLNotificationFilter filter);
+
virtual ~LLNotificationChannel() {}
typedef LLNotificationSet::iterator Iterator;
std::string getName() const { return mName; }
- std::string getParentChannelName() { return mParent; }
+ typedef std::vector<std::string>::const_iterator parents_iter;
+ boost::iterator_range<parents_iter> getParents() const
+ {
+ return boost::iterator_range<parents_iter>(mParents);
+ }
+
+ void connectToChannel(const std::string& channel_name);
bool isEmpty() const;
+ S32 size() const;
Iterator begin();
Iterator end();
-
- // Channels have a comparator to control sort order;
- // the default sorts by arrival date
- void setComparator(LLNotificationComparator comparator);
+ size_t size();
std::string summarize();
- // factory method for constructing these channels; since they're self-registering,
- // we want to make sure that you can't use new to make them
- static LLNotificationChannelPtr buildChannel(const std::string& name, const std::string& parent,
- LLNotificationFilter filter=LLNotificationFilters::includeEverything,
- LLNotificationComparator comparator=LLNotificationComparators::orderByUUID());
-
-protected:
- // Notification Channels have a filter, which determines which notifications
- // will be added to this channel.
- // Channel filters cannot change.
- // Channels have a protected constructor so you can't make smart pointers that don't
- // come from our internal reference; call NotificationChannel::build(args)
- LLNotificationChannel(const std::string& name, const std::string& parent,
- LLNotificationFilter filter, LLNotificationComparator comparator);
-
private:
std::string mName;
- std::string mParent;
- LLNotificationComparator mComparator;
+ std::vector<std::string> mParents;
};
// An interface class to provide a clean linker seam to the LLNotifications class.
@@ -863,10 +883,18 @@ class LLNotifications :
friend class LLSingleton<LLNotifications>;
public:
- // load notification descriptions from file;
- // OK to call more than once because it will reload
- bool loadTemplates();
-
+
+ // Needed to clear up RefCounted things prior to actual destruction
+ // as the singleton nature of the class makes them do "bad things"
+ // on at least Mac, if not all 3 platforms
+ //
+ void clear();
+
+ // load all notification descriptions from file
+ // calling more than once will overwrite existing templates
+ // but never delete a template
+ bool loadTemplates();
+
// load visibility rules from file;
// OK to call more than once because it will reload
bool loadVisibilityRules();
@@ -922,10 +950,6 @@ public:
void createDefaultChannels();
- typedef std::map<std::string, LLNotificationChannelPtr> ChannelMap;
- ChannelMap mChannels;
-
- void addChannel(LLNotificationChannelPtr pChan);
LLNotificationChannelPtr getChannel(const std::string& channelName);
std::string getGlobalString(const std::string& key) const;
@@ -950,8 +974,6 @@ private:
LLNotificationChannelPtr pHistoryChannel;
LLNotificationChannelPtr pExpirationChannel;
- // put your template in
- bool addTemplate(const std::string& name, LLNotificationTemplatePtr theTemplate);
TemplateMap mTemplates;
VisibilityRuleList mVisibilityRules;
@@ -965,7 +987,9 @@ private:
bool mIgnoreAllNotifications;
- boost::scoped_ptr<LLNotificationsListener> mListener;
+ boost::scoped_ptr<LLNotificationsListener> mListener;
+
+ std::vector<LLNotificationChannelPtr> mDefaultChannels;
};
/**
@@ -978,7 +1002,7 @@ private:
* 1 create class derived from LLPostponedNotification;
* 2 call LLPostponedNotification::add method;
*/
-class LLPostponedNotification
+class LLPostponedNotification : public LLMortician
{
public:
/**
@@ -996,26 +1020,38 @@ public:
thiz->mParams = params;
// Avoid header file dependency on llcachename.h
- lookupName(thiz, id, is_group);
+ thiz->lookupName(id, is_group);
}
private:
- static void lookupName(LLPostponedNotification* thiz, const LLUUID& id, bool is_group);
+ void lookupName(const LLUUID& id, bool is_group);
// only used for groups
void onGroupNameCache(const LLUUID& id, const std::string& full_name, bool is_group);
// only used for avatars
+ void fetchAvatarName(const LLUUID& id);
void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name);
// used for both group and avatar names
void finalizeName(const std::string& name);
void cleanup()
{
- delete this;
+ die();
}
protected:
- LLPostponedNotification() {}
- virtual ~LLPostponedNotification() {}
+ LLPostponedNotification()
+ : mParams(),
+ mName(),
+ mAvatarNameCacheConnection()
+ {}
+
+ virtual ~LLPostponedNotification()
+ {
+ if (mAvatarNameCacheConnection.connected())
+ {
+ mAvatarNameCacheConnection.disconnect();
+ }
+ }
/**
* Abstract method provides possibility to modify notification parameters and
@@ -1026,6 +1062,55 @@ protected:
LLNotification::Params mParams;
std::string mName;
+ boost::signals2::connection mAvatarNameCacheConnection;
+};
+
+// Stores only persistent notifications.
+// Class users can use connectChanged() to process persistent notifications
+// (see LLPersistentNotificationStorage for example).
+class LLPersistentNotificationChannel : public LLNotificationChannel
+{
+ LOG_CLASS(LLPersistentNotificationChannel);
+public:
+ LLPersistentNotificationChannel()
+ : LLNotificationChannel("Persistent", "Visible", &notificationFilter)
+ {}
+
+ typedef std::vector<LLNotificationPtr> history_list_t;
+ history_list_t::iterator beginHistory() { sortHistory(); return mHistory.begin(); }
+ history_list_t::iterator endHistory() { return mHistory.end(); }
+
+private:
+ struct sortByTime
+ {
+ S32 operator ()(const LLNotificationPtr& a, const LLNotificationPtr& b)
+ {
+ return a->getDate() < b->getDate();
+ }
+ };
+
+ void sortHistory()
+ {
+ std::sort(mHistory.begin(), mHistory.end(), sortByTime());
+ }
+
+ // The channel gets all persistent notifications except those that have been canceled
+ static bool notificationFilter(LLNotificationPtr pNotification)
+ {
+ bool handle_notification = false;
+
+ handle_notification = pNotification->isPersistent()
+ && !pNotification->isCancelled();
+
+ return handle_notification;
+ }
+
+ void onAdd(LLNotificationPtr p)
+ {
+ mHistory.push_back(p);
+ }
+
+ std::vector<LLNotificationPtr> mHistory;
};
#endif//LL_LLNOTIFICATIONS_H
diff --git a/indra/llui/llnotificationslistener.cpp b/indra/llui/llnotificationslistener.cpp
index 3bbeb3a778..b6a32a0e78 100644
--- a/indra/llui/llnotificationslistener.cpp
+++ b/indra/llui/llnotificationslistener.cpp
@@ -32,6 +32,7 @@
#include "llnotificationtemplate.h"
#include "llsd.h"
#include "llui.h"
+#include <boost/foreach.hpp>
LLNotificationsListener::LLNotificationsListener(LLNotifications & notifications) :
LLEventAPI("LLNotifications",
@@ -121,13 +122,18 @@ 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());
+ for (LLNotificationChannel::instance_iter cmi(LLNotificationChannel::beginInstances()),
+ cmend(LLNotificationChannel::endInstances());
cmi != cmend; ++cmi)
{
- LLSD channelInfo;
- channelInfo["parent"] = cmi->second->getParentChannelName();
- response[cmi->first] = channelInfo;
+ LLSD channelInfo, parents;
+ BOOST_FOREACH(const std::string& parent, cmi->getParents())
+ {
+ parents.append(parent);
+ }
+ channelInfo["parents"] = parents;
+ channelInfo["parent"] = parents.size()? parents[0] : "";
+ response[cmi->getName()] = channelInfo;
}
LLEventPumps::instance().obtain(params["reply"]).post(response);
}
diff --git a/indra/llui/llnotificationsutil.cpp b/indra/llui/llnotificationsutil.cpp
index cc791c26d1..cc791c26d1 100644..100755
--- a/indra/llui/llnotificationsutil.cpp
+++ b/indra/llui/llnotificationsutil.cpp
diff --git a/indra/llui/llnotificationsutil.h b/indra/llui/llnotificationsutil.h
index 4093324d0c..9f29087b4a 100644..100755
--- a/indra/llui/llnotificationsutil.h
+++ b/indra/llui/llnotificationsutil.h
@@ -30,6 +30,7 @@
// to avoid including the heavyweight llnotifications.h
#include "llnotificationptr.h"
+#include "lluuid.h"
#include <boost/function.hpp>
diff --git a/indra/llui/llnotificationtemplate.h b/indra/llui/llnotificationtemplate.h
index eff572b553..0315ddbea8 100644..100755
--- a/indra/llui/llnotificationtemplate.h
+++ b/indra/llui/llnotificationtemplate.h
@@ -28,39 +28,27 @@
#ifndef LL_LLNOTIFICATION_TEMPLATE_H
#define LL_LLNOTIFICATION_TEMPLATE_H
-//#include <string>
-//#include <list>
-//#include <vector>
-//#include <map>
-//#include <set>
-//#include <iomanip>
-//#include <sstream>
-//
-//#include <boost/utility.hpp>
-//#include <boost/shared_ptr.hpp>
-//#include <boost/enable_shared_from_this.hpp>
-//#include <boost/type_traits.hpp>
-//
-//// we want to minimize external dependencies, but this one is important
-//#include "llsd.h"
-//
-//// and we need this to manage the notification callbacks
-//#include "llevents.h"
-//#include "llfunctorregistry.h"
-//#include "llpointer.h"
#include "llinitparam.h"
-//#include "llnotificationslistener.h"
-//#include "llnotificationptr.h"
-//#include "llcachename.h"
#include "llnotifications.h"
-
typedef boost::shared_ptr<LLNotificationForm> LLNotificationFormPtr;
// This is the class of object read from the XML file (notifications.xml,
// from the appropriate local language directory).
struct LLNotificationTemplate
{
+ struct CombineBehaviorNames
+ : public LLInitParam::TypeValuesHelper<LLNotification::ECombineBehavior, CombineBehaviorNames>
+ {
+ static void declareValues()
+ {
+ declare("replace_with_new", LLNotification::REPLACE_WITH_NEW);
+ declare("keep_old", LLNotification::KEEP_OLD);
+ declare("cancel_old", LLNotification::CANCEL_OLD);
+ }
+ };
+
+
struct GlobalString : public LLInitParam::Block<GlobalString>
{
Mandatory<std::string> name,
@@ -88,15 +76,17 @@ struct LLNotificationTemplate
{
private:
// this idiom allows
- // <notification unique="true">
+ // <notification> <unique/> </notification>
// as well as
// <notification> <unique> <context></context> </unique>...
- Optional<bool> dummy_val;
+ Optional<LLInitParam::Flag> dummy_val;
public:
Multiple<UniquenessContext> contexts;
+ Optional<LLNotification::ECombineBehavior, CombineBehaviorNames> combine;
UniquenessConstraint()
: contexts("context"),
+ combine("combine", LLNotification::REPLACE_WITH_NEW),
dummy_val("")
{}
};
@@ -121,6 +111,7 @@ struct LLNotificationTemplate
Optional<std::string> yes_text,
no_text,
cancel_text,
+ help_text,
ignore_text;
TemplateRef()
@@ -128,6 +119,7 @@ struct LLNotificationTemplate
yes_text("yestext"),
no_text("notext"),
cancel_text("canceltext"),
+ help_text("helptext"),
ignore_text("ignoretext")
{}
};
@@ -147,7 +139,7 @@ struct LLNotificationTemplate
{}
};
- struct FormRef : public LLInitParam::Choice<FormRef>
+ struct FormRef : public LLInitParam::ChoiceBlock<FormRef>
{
Alternative<LLNotificationForm::Params> form;
Alternative<TemplateRef> form_template;
@@ -167,10 +159,24 @@ struct LLNotificationTemplate
{}
};
+ struct Footer : public LLInitParam::Block<Footer>
+ {
+ Mandatory<std::string> value;
+
+ Footer()
+ : value("value")
+ {
+ addSynonym(value, "");
+ }
+ };
+
struct Params : public LLInitParam::Block<Params>
{
Mandatory<std::string> name;
- Optional<bool> persist;
+ Optional<bool> persist,
+ log_to_im,
+ show_toast,
+ log_to_chat;
Optional<std::string> functor,
icon,
label,
@@ -184,12 +190,16 @@ struct LLNotificationTemplate
Optional<FormRef> form_ref;
Optional<ENotificationPriority,
NotificationPriorityValues> priority;
- Multiple<Tag> tags;
+ Multiple<Tag> tags;
+ Optional<Footer> footer;
Params()
: name("name"),
persist("persist", false),
+ log_to_im("log_to_im", false),
+ show_toast("show_toast", true),
+ log_to_chat("log_to_chat", true),
functor("functor"),
icon("icon"),
label("label"),
@@ -202,7 +212,8 @@ struct LLNotificationTemplate
url("url"),
unique("unique"),
form_ref(""),
- tags("tag")
+ tags("tag"),
+ footer("footer")
{}
};
@@ -231,6 +242,8 @@ struct LLNotificationTemplate
// The text used to display the notification. Replaceable parameters
// are enclosed in square brackets like this [].
std::string mMessage;
+ // The text used to display the notification, but under the form.
+ std::string mFooter;
// The label for the notification; used for
// certain classes of notification (those with a window and a window title).
// Also used when a notification pops up underneath the current one.
@@ -245,6 +258,7 @@ struct LLNotificationTemplate
// (used for things like progress indications, or repeating warnings
// like "the grid is going down in N minutes")
bool mUnique;
+ LLNotification::ECombineBehavior mCombineBehavior;
// if we want to be unique only if a certain part of the payload or substitutions args
// are constant specify the field names for the payload. The notification will only be
// combined if all of the fields named in the context are identical in the
@@ -285,12 +299,15 @@ struct LLNotificationTemplate
LLNotificationFormPtr mForm;
// default priority for notifications of this type
ENotificationPriority mPriority;
- // UUID of the audio file to be played when this notification arrives
- // this is loaded as a name, but looked up to get the UUID upon template load.
- // If null, it wasn't specified.
- LLUUID mSoundEffect;
+ // Stores the sound name which can then be used to play the sound using make_ui_sound
+ std::string mSoundName;
// List of tags that rules can match against.
std::list<std::string> mTags;
+
+ // inject these notifications into chat/IM streams
+ bool mLogToChat;
+ bool mLogToIM;
+ bool mShowToast;
};
#endif //LL_LLNOTIFICATION_TEMPLATE_H
diff --git a/indra/llui/llnotificationvisibilityrule.h b/indra/llui/llnotificationvisibilityrule.h
index 78bdec2a8f..78788a275c 100644..100755
--- a/indra/llui/llnotificationvisibilityrule.h
+++ b/indra/llui/llnotificationvisibilityrule.h
@@ -59,7 +59,7 @@ struct LLNotificationVisibilityRule
{}
};
- struct Rule : public LLInitParam::Choice<Rule>
+ struct Rule : public LLInitParam::ChoiceBlock<Rule>
{
Alternative<Filter> show;
Alternative<Filter> hide;
diff --git a/indra/llui/llpanel.cpp b/indra/llui/llpanel.cpp
index b2383106a8..ee90574161 100644..100755
--- a/indra/llui/llpanel.cpp
+++ b/indra/llui/llpanel.cpp
@@ -38,10 +38,8 @@
#include "lldir.h"
#include "lltimer.h"
-#include "llaccordionctrltab.h"
#include "llbutton.h"
#include "llmenugl.h"
-//#include "llstatusbar.h"
#include "llui.h"
#include "llkeyboard.h"
#include "lllineeditor.h"
@@ -50,7 +48,6 @@
#include "lluictrl.h"
#include "lluictrlfactory.h"
#include "llviewborder.h"
-#include "lltabcontainer.h"
static LLDefaultChildRegistry::Register<LLPanel> r1("panel", &LLPanel::fromXML);
LLPanel::factory_stack_t LLPanel::sFactoryStack;
@@ -87,9 +84,9 @@ LLPanel::Params::Params()
filename("filename"),
class_name("class"),
help_topic("help_topic"),
- visible_callback("visible_callback")
+ visible_callback("visible_callback"),
+ accepts_badge("accepts_badge")
{
- name = "panel";
addSynonym(background_visible, "bg_visible");
addSynonym(has_border, "border_visible");
addSynonym(label, "title");
@@ -98,6 +95,7 @@ LLPanel::Params::Params()
LLPanel::LLPanel(const LLPanel::Params& p)
: LLUICtrl(p),
+ LLBadgeHolder(p.accepts_badge),
mBgVisible(p.background_visible),
mBgOpaque(p.background_opaque),
mBgOpaqueColor(p.bg_opaque_color()),
@@ -121,8 +119,6 @@ LLPanel::LLPanel(const LLPanel::Params& p)
{
addBorder(p.border);
}
-
- mPanelHandle.bind(this);
}
LLPanel::~LLPanel()
@@ -167,8 +163,8 @@ void LLPanel::removeBorder()
// virtual
void LLPanel::clearCtrls()
{
- LLView::ctrl_list_t ctrls = getCtrlList();
- for (LLView::ctrl_list_t::iterator ctrl_it = ctrls.begin(); ctrl_it != ctrls.end(); ++ctrl_it)
+ LLPanel::ctrl_list_t ctrls = getCtrlList();
+ for (LLPanel::ctrl_list_t::iterator ctrl_it = ctrls.begin(); ctrl_it != ctrls.end(); ++ctrl_it)
{
LLUICtrl* ctrl = *ctrl_it;
ctrl->setFocus( FALSE );
@@ -179,14 +175,29 @@ void LLPanel::clearCtrls()
void LLPanel::setCtrlsEnabled( BOOL b )
{
- LLView::ctrl_list_t ctrls = getCtrlList();
- for (LLView::ctrl_list_t::iterator ctrl_it = ctrls.begin(); ctrl_it != ctrls.end(); ++ctrl_it)
+ LLPanel::ctrl_list_t ctrls = getCtrlList();
+ for (LLPanel::ctrl_list_t::iterator ctrl_it = ctrls.begin(); ctrl_it != ctrls.end(); ++ctrl_it)
{
LLUICtrl* ctrl = *ctrl_it;
ctrl->setEnabled( b );
}
}
+LLPanel::ctrl_list_t LLPanel::getCtrlList() const
+{
+ ctrl_list_t controls;
+ for(child_list_t::const_iterator it = getChildList()->begin(), end_it = getChildList()->end(); it != end_it; ++it)
+ {
+ LLView* viewp = *it;
+ if(viewp->isCtrl())
+ {
+ controls.push_back(static_cast<LLUICtrl*>(viewp));
+ }
+ }
+ return controls;
+}
+
+
void LLPanel::draw()
{
F32 alpha = getDrawContext().mAlpha;
@@ -343,9 +354,9 @@ BOOL LLPanel::handleKeyHere( KEY key, MASK mask )
return handled;
}
-void LLPanel::handleVisibilityChange ( BOOL new_visibility )
+void LLPanel::onVisibilityChange ( BOOL new_visibility )
{
- LLUICtrl::handleVisibilityChange ( new_visibility );
+ LLUICtrl::onVisibilityChange ( new_visibility );
if (mVisibleSignal)
(*mVisibleSignal)(this, LLSD(new_visibility) ); // Pass BOOL as LLSD
}
@@ -373,7 +384,7 @@ void LLPanel::setBorderVisible(BOOL b)
}
}
-LLFastTimer::DeclareTimer FTM_PANEL_CONSTRUCTION("Panel Construction");
+LLTrace::BlockTimerStatHandle FTM_PANEL_CONSTRUCTION("Panel Construction");
LLView* LLPanel::fromXML(LLXMLNodePtr node, LLView* parent, LLXMLNodePtr output_node)
{
@@ -385,14 +396,14 @@ LLView* LLPanel::fromXML(LLXMLNodePtr node, LLView* parent, LLXMLNodePtr output_
LLPanel* panelp = NULL;
- { LLFastTimer _(FTM_PANEL_CONSTRUCTION);
+ { LL_RECORD_BLOCK_TIME(FTM_PANEL_CONSTRUCTION);
if(!class_attr.empty())
{
panelp = LLRegisterPanelClass::instance().createPanelClass(class_attr);
if (!panelp)
{
- llwarns << "Panel class \"" << class_attr << "\" not registered." << llendl;
+ LL_WARNS() << "Panel class \"" << class_attr << "\" not registered." << LL_ENDL;
}
}
@@ -485,17 +496,19 @@ void LLPanel::initFromParams(const LLPanel::Params& p)
mBgAlphaImage = p.bg_alpha_image();
mBgOpaqueImageOverlay = p.bg_opaque_image_overlay;
mBgAlphaImageOverlay = p.bg_alpha_image_overlay;
+
+ setAcceptsBadge(p.accepts_badge);
}
-static LLFastTimer::DeclareTimer FTM_PANEL_SETUP("Panel Setup");
-static LLFastTimer::DeclareTimer FTM_EXTERNAL_PANEL_LOAD("Load Extern Panel Reference");
-static LLFastTimer::DeclareTimer FTM_PANEL_POSTBUILD("Panel PostBuild");
+static LLTrace::BlockTimerStatHandle FTM_PANEL_SETUP("Panel Setup");
+static LLTrace::BlockTimerStatHandle FTM_EXTERNAL_PANEL_LOAD("Load Extern Panel Reference");
+static LLTrace::BlockTimerStatHandle FTM_PANEL_POSTBUILD("Panel PostBuild");
BOOL LLPanel::initPanelXML(LLXMLNodePtr node, LLView *parent, LLXMLNodePtr output_node, const LLPanel::Params& default_params)
{
Params params(default_params);
{
- LLFastTimer timer(FTM_PANEL_SETUP);
+ LL_RECORD_BLOCK_TIME(FTM_PANEL_SETUP);
LLXMLNodePtr referenced_xml;
std::string xml_filename = mXMLFilename;
@@ -511,9 +524,6 @@ BOOL LLPanel::initPanelXML(LLXMLNodePtr node, LLView *parent, LLXMLNodePtr outpu
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
@@ -522,13 +532,16 @@ BOOL LLPanel::initPanelXML(LLXMLNodePtr node, LLView *parent, LLXMLNodePtr outpu
Params output_params(params);
setupParamsForExport(output_params, parent);
output_node->setName(node->getName()->mString);
- parser.writeXUI(output_node, output_params, &default_params);
+ parser.writeXUI(output_node, output_params, LLInitParam::default_parse_rules(), &default_params);
return TRUE;
}
+ LLUICtrlFactory::instance().pushFileName(xml_filename);
+
+ LL_RECORD_BLOCK_TIME(FTM_EXTERNAL_PANEL_LOAD);
if (!LLUICtrlFactory::getLayeredXMLNode(xml_filename, referenced_xml))
{
- llwarns << "Couldn't parse panel from: " << xml_filename << llendl;
+ LL_WARNS() << "Couldn't parse panel from: " << xml_filename << LL_ENDL;
return FALSE;
}
@@ -550,13 +563,13 @@ BOOL LLPanel::initPanelXML(LLXMLNodePtr node, LLView *parent, LLXMLNodePtr outpu
Params output_params(params);
setupParamsForExport(output_params, parent);
output_node->setName(node->getName()->mString);
- parser.writeXUI(output_node, output_params, &default_params);
+ parser.writeXUI(output_node, output_params, LLInitParam::default_parse_rules(), &default_params);
}
params.from_xui = true;
applyXUILayout(params, parent);
{
- LLFastTimer timer(FTM_PANEL_CONSTRUCTION);
+ LL_RECORD_BLOCK_TIME(FTM_PANEL_CONSTRUCTION);
initFromParams(params);
}
@@ -573,7 +586,7 @@ BOOL LLPanel::initPanelXML(LLXMLNodePtr node, LLView *parent, LLXMLNodePtr outpu
}
{
- LLFastTimer timer(FTM_PANEL_POSTBUILD);
+ LL_RECORD_BLOCK_TIME(FTM_PANEL_POSTBUILD);
postBuild();
}
}
@@ -598,11 +611,11 @@ std::string LLPanel::getString(const std::string& name, const LLStringUtil::form
std::string err_str("Failed to find string " + name + " in panel " + getName()); //*TODO: Translate
if(LLUI::sSettingGroups["config"]->getBOOL("QAMode"))
{
- llerrs << err_str << llendl;
+ LL_ERRS() << err_str << LL_ENDL;
}
else
{
- llwarns << err_str << llendl;
+ LL_WARNS() << err_str << LL_ENDL;
}
return LLStringUtil::null;
}
@@ -617,11 +630,11 @@ std::string LLPanel::getString(const std::string& name) const
std::string err_str("Failed to find string " + name + " in panel " + getName()); //*TODO: Translate
if(LLUI::sSettingGroups["config"]->getBOOL("QAMode"))
{
- llerrs << err_str << llendl;
+ LL_ERRS() << err_str << LL_ENDL;
}
else
{
- llwarns << err_str << llendl;
+ LL_WARNS() << err_str << LL_ENDL;
}
return LLStringUtil::null;
}
@@ -636,16 +649,6 @@ void LLPanel::childSetVisible(const std::string& id, bool visible)
}
}
-bool LLPanel::childIsVisible(const std::string& id) const
-{
- LLView* child = findChild<LLView>(id);
- if (child)
- {
- return (bool)child->getVisible();
- }
- return false;
-}
-
void LLPanel::childSetEnabled(const std::string& id, bool enabled)
{
LLView* child = findChild<LLView>(id);
@@ -655,55 +658,6 @@ void LLPanel::childSetEnabled(const std::string& id, bool enabled)
}
}
-void LLPanel::childSetTentative(const std::string& id, bool tentative)
-{
- LLUICtrl* child = findChild<LLUICtrl>(id);
- if (child)
- {
- child->setTentative(tentative);
- }
-}
-
-bool LLPanel::childIsEnabled(const std::string& id) const
-{
- LLView* child = findChild<LLView>(id);
- if (child)
- {
- return (bool)child->getEnabled();
- }
- return false;
-}
-
-
-void LLPanel::childSetToolTip(const std::string& id, const std::string& msg)
-{
- LLView* child = findChild<LLView>(id);
- if (child)
- {
- child->setToolTip(msg);
- }
-}
-
-void LLPanel::childSetRect(const std::string& id, const LLRect& rect)
-{
- LLView* child = findChild<LLView>(id);
- if (child)
- {
- child->setRect(rect);
- }
-}
-
-bool LLPanel::childGetRect(const std::string& id, LLRect& rect) const
-{
- LLView* child = findChild<LLView>(id);
- if (child)
- {
- rect = child->getRect();
- return true;
- }
- return false;
-}
-
void LLPanel::childSetFocus(const std::string& id, BOOL focus)
{
LLUICtrl* child = findChild<LLUICtrl>(id);
@@ -739,15 +693,6 @@ void LLPanel::childSetCommitCallback(const std::string& id, boost::function<void
}
}
-void LLPanel::childSetValidate(const std::string& id, boost::function<bool (const LLSD& data)> cb)
-{
- LLUICtrl* child = findChild<LLUICtrl>(id);
- if (child)
- {
- child->setValidateBeforeCommit(cb);
- }
-}
-
void LLPanel::childSetColor(const std::string& id, const LLColor4& color)
{
LLUICtrl* child = findChild<LLUICtrl>(id);
@@ -827,95 +772,6 @@ BOOL LLPanel::childSetLabelArg(const std::string& id, const std::string& key, co
return FALSE;
}
-BOOL LLPanel::childSetToolTipArg(const std::string& id, const std::string& key, const LLStringExplicit& text)
-{
- LLView* child = findChild<LLView>(id);
- if (child)
- {
- return child->setToolTipArg(key, text);
- }
- return FALSE;
-}
-
-void LLPanel::childShowTab(const std::string& id, const std::string& tabname, bool visible)
-{
- LLTabContainer* child = findChild<LLTabContainer>(id);
- if (child)
- {
- child->selectTabByName(tabname);
- }
-}
-
-LLPanel *LLPanel::childGetVisibleTab(const std::string& id) const
-{
- LLTabContainer* child = findChild<LLTabContainer>(id);
- if (child)
- {
- return child->getCurrentPanel();
- }
- return NULL;
-}
-
-LLPanel* LLPanel::childGetVisibleTabWithHelp()
-{
- LLView *child;
-
- bfs_tree_iterator_t it = beginTreeBFS();
- // skip ourselves
- ++it;
- for (; it != endTreeBFS(); ++it)
- {
- 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;
-}
-
-
-LLPanel *LLPanel::childGetVisiblePanelWithHelp()
-{
- LLView *child;
-
- bfs_tree_iterator_t it = beginTreeBFS();
- // skip ourselves
- ++it;
- for (; it != endTreeBFS(); ++it)
- {
- child = *it;
- // do we have a panel with a help topic?
- LLPanel *panel = dynamic_cast<LLPanel *>(child);
- if (panel && panel->isInVisibleChain() && !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);
@@ -934,24 +790,6 @@ void LLPanel::childSetAction(const std::string& id, boost::function<void(void*)>
}
}
-void LLPanel::childSetActionTextbox(const std::string& id, boost::function<void(void*)> function, void* value)
-{
- LLTextBox* textbox = findChild<LLTextBox>(id);
- if (textbox)
- {
- textbox->setClickedCallback(boost::bind(function, value));
- }
-}
-
-void LLPanel::childSetControlName(const std::string& id, const std::string& control_name)
-{
- LLUICtrl* view = findChild<LLUICtrl>(id);
- if (view)
- {
- view->setControlName(control_name, NULL);
- }
-}
-
boost::signals2::connection LLPanel::setVisibleCallback( const commit_signal_t::slot_type& cb )
{
if (!mVisibleSignal)
@@ -962,41 +800,31 @@ boost::signals2::connection LLPanel::setVisibleCallback( const commit_signal_t::
return mVisibleSignal->connect(cb);
}
-static LLFastTimer::DeclareTimer FTM_BUILD_PANELS("Build Panels");
+static LLTrace::BlockTimerStatHandle FTM_BUILD_PANELS("Build Panels");
//-----------------------------------------------------------------------------
// buildPanel()
//-----------------------------------------------------------------------------
-BOOL LLPanel::buildFromFile(const std::string& filename, LLXMLNodePtr output_node, const LLPanel::Params& default_params)
+BOOL LLPanel::buildFromFile(const std::string& filename, const LLPanel::Params& default_params)
{
- LLFastTimer timer(FTM_BUILD_PANELS);
+ LL_RECORD_BLOCK_TIME(FTM_BUILD_PANELS);
BOOL didPost = FALSE;
LLXMLNodePtr root;
- //if exporting, only load the language being exported,
- //instead of layering localized version on top of english
- if (output_node)
- {
- if (!LLUICtrlFactory::getLocalizedXMLNode(filename, root))
- {
- llwarns << "Couldn't parse panel from: " << LLUI::getLocalizedSkinPath() + gDirUtilp->getDirDelimiter() + filename << llendl;
- return didPost;
- }
- }
- else if (!LLUICtrlFactory::getLayeredXMLNode(filename, root))
+ if (!LLUICtrlFactory::getLayeredXMLNode(filename, root))
{
- llwarns << "Couldn't parse panel from: " << LLUI::getSkinPath() + gDirUtilp->getDirDelimiter() + filename << llendl;
+ LL_WARNS() << "Couldn't parse panel from: " << filename << LL_ENDL;
return didPost;
}
// root must be called panel
if( !root->hasName("panel" ) )
{
- llwarns << "Root node should be named panel in : " << filename << llendl;
+ LL_WARNS() << "Root node should be named panel in : " << filename << LL_ENDL;
return didPost;
}
- lldebugs << "Building panel " << filename << llendl;
+ LL_DEBUGS() << "Building panel " << filename << LL_ENDL;
LLUICtrlFactory::instance().pushFileName(filename);
{
@@ -1009,7 +837,7 @@ BOOL LLPanel::buildFromFile(const std::string& filename, LLXMLNodePtr output_nod
getCommitCallbackRegistrar().pushScope();
getEnableCallbackRegistrar().pushScope();
- didPost = initPanelXML(root, NULL, output_node, default_params);
+ didPost = initPanelXML(root, NULL, NULL, default_params);
getCommitCallbackRegistrar().popScope();
getEnableCallbackRegistrar().popScope();
diff --git a/indra/llui/llpanel.h b/indra/llui/llpanel.h
index c1a1a06f39..c2185f24de 100644..100755
--- a/indra/llui/llpanel.h
+++ b/indra/llui/llpanel.h
@@ -32,8 +32,10 @@
#include "llcallbackmap.h"
#include "lluictrl.h"
#include "llviewborder.h"
+#include "lluiimage.h"
#include "lluistring.h"
#include "v4color.h"
+#include "llbadgeholder.h"
#include <list>
#include <queue>
@@ -50,7 +52,7 @@ class LLUIImage;
* With or without border,
* Can contain LLUICtrls.
*/
-class LLPanel : public LLUICtrl
+class LLPanel : public LLUICtrl, public LLBadgeHolder
{
public:
struct LocalizedString : public LLInitParam::Block<LocalizedString>
@@ -88,13 +90,12 @@ public:
Multiple<LocalizedString> strings;
Optional<CommitCallbackParam> visible_callback;
+
+ Optional<bool> accepts_badge;
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
@@ -104,7 +105,9 @@ protected:
LLPanel(const LLPanel::Params& params = getDefaultParams());
public:
- BOOL buildFromFile(const std::string &filename, LLXMLNodePtr output_node = NULL, const LLPanel::Params&default_params = getDefaultParams());
+ typedef std::vector<class LLUICtrl *> ctrl_list_t;
+
+ BOOL buildFromFile(const std::string &filename, const LLPanel::Params& default_params = getDefaultParams());
static LLPanel* createFactoryPanel(const std::string& name);
@@ -114,7 +117,7 @@ public:
/*virtual*/ BOOL isPanel() const;
/*virtual*/ void draw();
/*virtual*/ BOOL handleKeyHere( KEY key, MASK mask );
- /*virtual*/ void handleVisibilityChange ( BOOL new_visibility );
+ /*virtual*/ void onVisibilityChange ( BOOL new_visibility );
// From LLFocusableElement
/*virtual*/ void setFocus( BOOL b );
@@ -134,6 +137,8 @@ public:
const LLColor4& getBackgroundColor() const { return mBgOpaqueColor; }
void setTransparentColor(const LLColor4& color) { mBgAlphaColor = color; }
const LLColor4& getTransparentColor() const { return mBgAlphaColor; }
+ void setBackgroundImage(LLUIImage* image) { mBgOpaqueImage = image; }
+ void setTransparentImage(LLUIImage* image) { mBgAlphaImage = image; }
LLPointer<LLUIImage> getBackgroundImage() const { return mBgOpaqueImage; }
LLPointer<LLUIImage> getTransparentImage() const { return mBgAlphaImage; }
LLColor4 getBackgroundImageOverlay() { return mBgOpaqueImageOverlay; }
@@ -151,8 +156,9 @@ public:
std::string getHelpTopic() const { return mHelpTopic; }
void setCtrlsEnabled(BOOL b);
+ ctrl_list_t getCtrlList() const;
- LLHandle<LLPanel> getHandle() const { return mPanelHandle; }
+ LLHandle<LLPanel> getHandle() const { return getDerivedHandle<LLPanel>(); }
const LLCallbackMap::map_t& getFactoryMap() const { return mFactoryMap; }
@@ -171,19 +177,10 @@ public:
// LLView
void childSetVisible(const std::string& name, bool visible);
- void childShow(const std::string& name) { childSetVisible(name, true); }
- void childHide(const std::string& name) { childSetVisible(name, false); }
- bool childIsVisible(const std::string& id) const;
- void childSetTentative(const std::string& name, bool tentative);
void childSetEnabled(const std::string& name, bool enabled);
void childEnable(const std::string& name) { childSetEnabled(name, true); }
void childDisable(const std::string& name) { childSetEnabled(name, false); };
- bool childIsEnabled(const std::string& id) const;
-
- void childSetToolTip(const std::string& id, const std::string& msg);
- void childSetRect(const std::string& id, const LLRect &rect);
- bool childGetRect(const std::string& id, LLRect& rect) const;
// LLUICtrl
void childSetFocus(const std::string& id, BOOL focus = TRUE);
@@ -194,9 +191,6 @@ public:
// 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);
LLCtrlSelectionInterface* childGetSelectionInterface(const std::string& id) const;
@@ -211,34 +205,11 @@ public:
// Not implemented for all types, defaults to noop, returns FALSE if not applicaple
BOOL childSetTextArg(const std::string& id, const std::string& key, const LLStringExplicit& text);
BOOL childSetLabelArg(const std::string& id, const std::string& key, const LLStringExplicit& text);
- BOOL childSetToolTipArg(const std::string& id, const std::string& key, const LLStringExplicit& text);
- // LLTabContainer
- void childShowTab(const std::string& id, const std::string& tabname, bool visible = true);
- LLPanel *childGetVisibleTab(const std::string& id) const;
-
- // 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)); }
-
- // *NOTE: Does not return text from <string> tags, use getString()
- std::string childGetText(const std::string& id) const { return childGetValue(id).asString(); }
-
- // LLLineEditor
- void childSetPrevalidate(const std::string& id, bool (*func)(const LLWString &) );
-
// LLButton
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);
- // 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
@@ -261,6 +232,9 @@ protected:
std::string mHelpTopic; // the name of this panel's help topic to display in the Help Viewer
typedef std::deque<const LLCallbackMap::map_t*> factory_stack_t;
static factory_stack_t sFactoryStack;
+
+ // for setting the xml filename when building panel in context dependent cases
+ std::string mXMLFilename;
private:
BOOL mBgVisible; // any background at all?
@@ -274,13 +248,10 @@ private:
LLViewBorder* mBorder;
LLButton* mDefaultBtn;
LLUIString mLabel;
- LLRootHandle<LLPanel> mPanelHandle;
typedef std::map<std::string, std::string> ui_string_map_t;
ui_string_map_t mUIStrings;
- // for setting the xml filename when building panel in context dependent cases
- std::string mXMLFilename;
}; // end class LLPanel
@@ -327,17 +298,16 @@ private:
// local static instance for registering a particular panel class
template<typename T>
-class LLRegisterPanelClassWrapper
-: public LLRegisterPanelClass
+ class LLPanelInjector
{
public:
- // reigister with either the provided builder, or the generic templated builder
- LLRegisterPanelClassWrapper(const std::string& tag);
+ // register with either the provided builder, or the generic templated builder
+ LLPanelInjector(const std::string& tag);
};
template<typename T>
-LLRegisterPanelClassWrapper<T>::LLRegisterPanelClassWrapper(const std::string& tag)
+ LLPanelInjector<T>::LLPanelInjector(const std::string& tag)
{
LLRegisterPanelClass::instance().addPanelClass(tag,&LLRegisterPanelClass::defaultPanelClassBuilder<T>);
}
diff --git a/indra/llui/llprogressbar.cpp b/indra/llui/llprogressbar.cpp
index ead22686bc..209796565c 100644..100755
--- a/indra/llui/llprogressbar.cpp
+++ b/indra/llui/llprogressbar.cpp
@@ -38,6 +38,7 @@
#include "llfocusmgr.h"
#include "lluictrlfactory.h"
+#include "lluiimage.h"
static LLDefaultChildRegistry::Register<LLProgressBar> r("progress_bar");
@@ -76,7 +77,7 @@ void LLProgressBar::draw()
LLColor4 bar_color = mColorBar.get();
bar_color.mV[VALPHA] *= alpha; // modulate alpha
LLRect progress_rect = getLocalRect();
- progress_rect.mRight = llround(getRect().getWidth() * (mPercentDone / 100.f));
+ progress_rect.mRight = ll_round(getRect().getWidth() * (mPercentDone / 100.f));
mImageFill->draw(progress_rect, bar_color);
}
diff --git a/indra/llui/llprogressbar.h b/indra/llui/llprogressbar.h
index 3f308e7496..a8ec83ea00 100644..100755
--- a/indra/llui/llprogressbar.h
+++ b/indra/llui/llprogressbar.h
@@ -27,8 +27,9 @@
#ifndef LL_LLPROGRESSBAR_H
#define LL_LLPROGRESSBAR_H
-#include "lluictrl.h"
#include "llframetimer.h"
+#include "lluictrl.h"
+#include "lluiimage.h"
class LLProgressBar
: public LLUICtrl
diff --git a/indra/llui/llradiogroup.cpp b/indra/llui/llradiogroup.cpp
index 6e9586369f..b53bb16d97 100644..100755
--- a/indra/llui/llradiogroup.cpp
+++ b/indra/llui/llradiogroup.cpp
@@ -74,9 +74,6 @@ LLRadioGroup::Params::Params()
{
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;
}
@@ -96,7 +93,10 @@ void LLRadioGroup::initFromParams(const Params& p)
{
LLRadioGroup::ItemParams item_params(*it);
- item_params.font.setIfNotProvided(mFont); // apply radio group font by default
+ if (!item_params.font.isProvided())
+ {
+ item_params.font = mFont; // apply radio group font by default
+ }
item_params.commit_callback.function = boost::bind(&LLRadioGroup::onClickButton, this, _1);
item_params.from_xui = p.from_xui;
if (p.from_xui)
@@ -289,7 +289,7 @@ BOOL LLRadioGroup::handleMouseDown(S32 x, S32 y, MASK mask)
void LLRadioGroup::onClickButton(LLUICtrl* ctrl)
{
- // llinfos << "LLRadioGroup::onClickButton" << llendl;
+ // LL_INFOS() << "LLRadioGroup::onClickButton" << LL_ENDL;
LLRadioCtrl* clicked_radio = dynamic_cast<LLRadioCtrl*>(ctrl);
if (!clicked_radio)
return;
@@ -319,7 +319,7 @@ void LLRadioGroup::onClickButton(LLUICtrl* ctrl)
index++;
}
- llwarns << "LLRadioGroup::onClickButton - clicked button that isn't a child" << llendl;
+ LL_WARNS() << "LLRadioGroup::onClickButton - clicked button that isn't a child" << LL_ENDL;
}
void LLRadioGroup::setValue( const LLSD& value )
@@ -346,7 +346,7 @@ void LLRadioGroup::setValue( const LLSD& value )
}
else
{
- llwarns << "LLRadioGroup::setValue: value not found: " << value.asString() << llendl;
+ setSelectedIndex(-1, TRUE);
}
}
}
diff --git a/indra/llui/llradiogroup.h b/indra/llui/llradiogroup.h
index 8bd5698538..8bd5698538 100644..100755
--- a/indra/llui/llradiogroup.h
+++ b/indra/llui/llradiogroup.h
diff --git a/indra/llui/llresizebar.cpp b/indra/llui/llresizebar.cpp
index 02f60c76fa..115c4e23be 100644..100755
--- a/indra/llui/llresizebar.cpp
+++ b/indra/llui/llresizebar.cpp
@@ -28,12 +28,23 @@
#include "llresizebar.h"
+#include "lllocalcliprect.h"
#include "llmath.h"
#include "llui.h"
#include "llmenugl.h"
#include "llfocusmgr.h"
#include "llwindow.h"
+LLResizeBar::Params::Params()
+: max_size("max_size", S32_MAX),
+ snapping_enabled("snapping_enabled", true),
+ resizing_view("resizing_view"),
+ side("side"),
+ allow_double_click_snapping("allow_double_click_snapping", true)
+{
+ name = "resize_bar";
+}
+
LLResizeBar::LLResizeBar(const LLResizeBar::Params& p)
: LLView(p),
mDragLastScreenX( 0 ),
@@ -45,7 +56,9 @@ LLResizeBar::LLResizeBar(const LLResizeBar::Params& p)
mSide( p.side ),
mSnappingEnabled(p.snapping_enabled),
mAllowDoubleClickSnapping(p.allow_double_click_snapping),
- mResizingView(p.resizing_view)
+ mResizingView(p.resizing_view),
+ mResizeListener(NULL),
+ mImagePanel(NULL)
{
setFollowsNone();
// set up some generically good follow code.
@@ -76,9 +89,10 @@ LLResizeBar::LLResizeBar(const LLResizeBar::Params& p)
}
}
-
BOOL LLResizeBar::handleMouseDown(S32 x, S32 y, MASK mask)
{
+ if (!canResize()) return FALSE;
+
// Route future Mouse messages here preemptively. (Release on mouse up.)
// No handler needed for focus lost since this clas has no state that depends on it.
gFocusMgr.setMouseCapture( this );
@@ -137,13 +151,6 @@ 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;
@@ -217,20 +224,66 @@ BOOL LLResizeBar::handleHover(S32 x, S32 y, MASK mask)
// update last valid mouse cursor position based on resized view's actual size
LLRect new_rect = mResizingView->getRect();
+
switch(mSide)
{
case LEFT:
- mDragLastScreenX += new_rect.mLeft - orig_rect.mLeft;
+ {
+ S32 actual_delta_x = new_rect.mLeft - orig_rect.mLeft;
+ if (actual_delta_x != delta_x)
+ {
+ // restore everything by left
+ new_rect.mBottom = orig_rect.mBottom;
+ new_rect.mTop = orig_rect.mTop;
+ new_rect.mRight = orig_rect.mRight;
+ mResizingView->setShape(new_rect, true);
+ }
+ mDragLastScreenX += actual_delta_x;
+
break;
+ }
case RIGHT:
+ {
+ S32 actual_delta_x = new_rect.mRight - orig_rect.mRight;
+ if (actual_delta_x != delta_x)
+ {
+ // restore everything by left
+ new_rect.mBottom = orig_rect.mBottom;
+ new_rect.mTop = orig_rect.mTop;
+ new_rect.mLeft = orig_rect.mLeft;
+ mResizingView->setShape(new_rect, true);
+ }
mDragLastScreenX += new_rect.mRight - orig_rect.mRight;
break;
+ }
case TOP:
+ {
+ S32 actual_delta_y = new_rect.mTop - orig_rect.mTop;
+ if (actual_delta_y != delta_y)
+ {
+ // restore everything by left
+ new_rect.mBottom = orig_rect.mBottom;
+ new_rect.mLeft = orig_rect.mLeft;
+ new_rect.mRight = orig_rect.mRight;
+ mResizingView->setShape(new_rect, true);
+ }
mDragLastScreenY += new_rect.mTop - orig_rect.mTop;
break;
+ }
case BOTTOM:
+ {
+ S32 actual_delta_y = new_rect.mBottom - orig_rect.mBottom;
+ if (actual_delta_y != delta_y)
+ {
+ // restore everything by left
+ new_rect.mTop = orig_rect.mTop;
+ new_rect.mLeft = orig_rect.mLeft;
+ new_rect.mRight = orig_rect.mRight;
+ mResizingView->setShape(new_rect, true);
+ }
mDragLastScreenY += new_rect.mBottom- orig_rect.mBottom;
break;
+ }
default:
break;
}
@@ -243,7 +296,7 @@ BOOL LLResizeBar::handleHover(S32 x, S32 y, MASK mask)
handled = TRUE;
}
- if( handled )
+ if( handled && canResize() )
{
switch( mSide )
{
@@ -259,6 +312,11 @@ BOOL LLResizeBar::handleHover(S32 x, S32 y, MASK mask)
}
}
+ if (mResizeListener)
+ {
+ mResizeListener(NULL);
+ }
+
return handled;
} // end LLResizeBar::handleHover
@@ -295,3 +353,24 @@ BOOL LLResizeBar::handleDoubleClick(S32 x, S32 y, MASK mask)
return TRUE;
}
+void LLResizeBar::setImagePanel(LLPanel * panelp)
+{
+ const LLView::child_list_t * children = getChildList();
+ if (getChildCount() == 2)
+ {
+ LLPanel * image_panelp = dynamic_cast<LLPanel*>(children->back());
+ if (image_panelp)
+ {
+ removeChild(image_panelp);
+ delete image_panelp;
+ }
+ }
+
+ addChild(panelp);
+ sendChildToBack(panelp);
+}
+
+LLPanel * LLResizeBar::getImagePanel() const
+{
+ return getChildCount() > 0 ? (LLPanel *)getChildList()->back() : NULL;
+}
diff --git a/indra/llui/llresizebar.h b/indra/llui/llresizebar.h
index 0725fbd846..20a2406484 100644..100755
--- a/indra/llui/llresizebar.h
+++ b/indra/llui/llresizebar.h
@@ -28,7 +28,6 @@
#define LL_RESIZEBAR_H
#include "llview.h"
-#include "llcoord.h"
class LLResizeBar : public LLView
{
@@ -45,23 +44,15 @@ public:
Optional<bool> snapping_enabled;
Optional<bool> allow_double_click_snapping;
- Params()
- : max_size("max_size", S32_MAX),
- snapping_enabled("snapping_enabled", true),
- resizing_view("resizing_view"),
- side("side"),
- allow_double_click_snapping("allow_double_click_snapping", true)
- {
- name = "resize_bar";
- }
+ Params();
};
protected:
LLResizeBar(const LLResizeBar::Params& p);
friend class LLUICtrlFactory;
+
public:
-// virtual void draw(); No appearance
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);
@@ -70,19 +61,26 @@ public:
void setResizeLimits( S32 min_size, S32 max_size ) { mMinSize = min_size; mMaxSize = max_size; }
void setEnableSnapping(BOOL enable) { mSnappingEnabled = enable; }
void setAllowDoubleClickSnapping(BOOL allow) { mAllowDoubleClickSnapping = allow; }
+ bool canResize() { return getEnabled() && mMaxSize > mMinSize; }
+ void setResizeListener(boost::function<void(void*)> listener) {mResizeListener = listener;}
+ void setImagePanel(LLPanel * panelp);
+ LLPanel * getImagePanel() const;
private:
- S32 mDragLastScreenX;
- S32 mDragLastScreenY;
- S32 mLastMouseScreenX;
- S32 mLastMouseScreenY;
- LLCoordGL mLastMouseDir;
- S32 mMinSize;
- S32 mMaxSize;
- const Side mSide;
- BOOL mSnappingEnabled;
- BOOL mAllowDoubleClickSnapping;
- LLView* mResizingView;
+ S32 mDragLastScreenX;
+ S32 mDragLastScreenY;
+ S32 mLastMouseScreenX;
+ S32 mLastMouseScreenY;
+ LLCoordGL mLastMouseDir;
+ S32 mMinSize;
+ S32 mMaxSize;
+ const Side mSide;
+ bool mSnappingEnabled,
+ mAllowDoubleClickSnapping;
+ LLView* mResizingView;
+ boost::function<void(void*)> mResizeListener;
+ LLPointer<LLUIImage> mDragHandleImage;
+ LLPanel * mImagePanel;
};
#endif // LL_RESIZEBAR_H
diff --git a/indra/llui/llresizehandle.cpp b/indra/llui/llresizehandle.cpp
index c3a51c36c9..24794305ac 100644..100755
--- a/indra/llui/llresizehandle.cpp
+++ b/indra/llui/llresizehandle.cpp
@@ -257,23 +257,65 @@ BOOL LLResizeHandle::handleHover(S32 x, S32 y, MASK mask)
// update last valid mouse cursor position based on resized view's actual size
LLRect new_rect = resizing_view->getRect();
+ S32 actual_delta_x = 0;
+ S32 actual_delta_y = 0;
switch(mCorner)
{
case LEFT_TOP:
- mDragLastScreenX += new_rect.mLeft - orig_rect.mLeft;
- mDragLastScreenY += new_rect.mTop - orig_rect.mTop;
+ actual_delta_x = new_rect.mLeft - orig_rect.mLeft;
+ actual_delta_y = new_rect.mTop - orig_rect.mTop;
+ if (actual_delta_x != delta_x
+ || actual_delta_y != delta_y)
+ {
+ new_rect.mRight = orig_rect.mRight;
+ new_rect.mBottom = orig_rect.mBottom;
+ resizing_view->setShape(new_rect, true);
+ }
+
+ mDragLastScreenX += actual_delta_x;
+ mDragLastScreenY += actual_delta_y;
break;
case LEFT_BOTTOM:
- mDragLastScreenX += new_rect.mLeft - orig_rect.mLeft;
- mDragLastScreenY += new_rect.mBottom- orig_rect.mBottom;
+ actual_delta_x = new_rect.mLeft - orig_rect.mLeft;
+ actual_delta_y = new_rect.mBottom - orig_rect.mBottom;
+ if (actual_delta_x != delta_x
+ || actual_delta_y != delta_y)
+ {
+ new_rect.mRight = orig_rect.mRight;
+ new_rect.mTop = orig_rect.mTop;
+ resizing_view->setShape(new_rect, true);
+ }
+
+ mDragLastScreenX += actual_delta_x;
+ mDragLastScreenY += actual_delta_y;
break;
case RIGHT_TOP:
- mDragLastScreenX += new_rect.mRight - orig_rect.mRight;
- mDragLastScreenY += new_rect.mTop - orig_rect.mTop;
+ actual_delta_x = new_rect.mRight - orig_rect.mRight;
+ actual_delta_y = new_rect.mTop - orig_rect.mTop;
+ if (actual_delta_x != delta_x
+ || actual_delta_y != delta_y)
+ {
+ new_rect.mLeft = orig_rect.mLeft;
+ new_rect.mBottom = orig_rect.mBottom;
+ resizing_view->setShape(new_rect, true);
+ }
+
+ mDragLastScreenX += actual_delta_x;
+ mDragLastScreenY += actual_delta_y;
break;
case RIGHT_BOTTOM:
- mDragLastScreenX += new_rect.mRight - orig_rect.mRight;
- mDragLastScreenY += new_rect.mBottom- orig_rect.mBottom;
+ actual_delta_x = new_rect.mRight - orig_rect.mRight;
+ actual_delta_y = new_rect.mBottom - orig_rect.mBottom;
+ if (actual_delta_x != delta_x
+ || actual_delta_y != delta_y)
+ {
+ new_rect.mLeft = orig_rect.mLeft;
+ new_rect.mTop = orig_rect.mTop;
+ resizing_view->setShape(new_rect, true);
+ }
+
+ mDragLastScreenX += actual_delta_x;
+ mDragLastScreenY += actual_delta_y;
break;
default:
break;
diff --git a/indra/llui/llresizehandle.h b/indra/llui/llresizehandle.h
index 531eb1db61..7541b9e6c0 100644..100755
--- a/indra/llui/llresizehandle.h
+++ b/indra/llui/llresizehandle.h
@@ -55,7 +55,7 @@ public:
virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
void setResizeLimits( S32 min_width, S32 min_height ) { mMinWidth = min_width; mMinHeight = min_height; }
-
+
private:
BOOL pointInHandle( S32 x, S32 y );
diff --git a/indra/llui/llresmgr.cpp b/indra/llui/llresmgr.cpp
index 39385786bc..6e924c1f19 100644..100755
--- a/indra/llui/llresmgr.cpp
+++ b/indra/llui/llresmgr.cpp
@@ -45,33 +45,6 @@ LLResMgr::LLResMgr()
void LLResMgr::setLocale( LLLOCALE_ID locale_id )
{
mLocale = locale_id;
-
- //RN: for now, use normal 'C' locale for everything but specific UI input/output routines
-// switch( locale_id )
-// {
-// case LLLOCALE_USA:
-//#if LL_WINDOWS
-// // Windows doesn't use ISO country codes.
-// llinfos << "Setting locale to " << setlocale( LC_ALL, "english-usa" ) << llendl;
-//#else
-// // posix version should work everywhere else.
-// llinfos << "Setting locale to " << setlocale( LC_ALL, "en_US" ) << llendl;
-//#endif
-// break;
-// case LLLOCALE_UK:
-//#if LL_WINDOWS
-// // Windows doesn't use ISO country codes.
-// llinfos << "Setting locale to " << setlocale( LC_ALL, "english-uk" ) << llendl;
-//#else
-// // posix version should work everywhere else.
-// llinfos << "Setting locale to " << setlocale( LC_ALL, "en_GB" ) << llendl;
-//#endif
-// break;
-// default:
-// llassert(0);
-// setLocale(LLLOCALE_USA);
-// break;
-// }
}
char LLResMgr::getDecimalPoint() const
@@ -337,12 +310,12 @@ LLLocale::LLLocale(const std::string& locale_string)
char* new_locale_string = setlocale( LC_ALL, locale_string.c_str());
if ( new_locale_string == NULL)
{
- llwarns << "Failed to set locale " << locale_string << llendl;
+ LL_WARNS_ONCE("LLLocale") << "Failed to set locale " << locale_string << LL_ENDL;
setlocale(LC_ALL, SYSTEM_LOCALE.c_str());
}
//else
//{
- // llinfos << "Set locale to " << new_locale_string << llendl;
+ // LL_INFOS() << "Set locale to " << new_locale_string << LL_ENDL;
//}
}
diff --git a/indra/llui/llresmgr.h b/indra/llui/llresmgr.h
index a652dcd2c0..a652dcd2c0 100644..100755
--- a/indra/llui/llresmgr.h
+++ b/indra/llui/llresmgr.h
diff --git a/indra/llui/llrngwriter.cpp b/indra/llui/llrngwriter.cpp
index 5e6840d7df..e4a31d6a79 100644..100755
--- a/indra/llui/llrngwriter.cpp
+++ b/indra/llui/llrngwriter.cpp
@@ -28,7 +28,17 @@
#include "llrngwriter.h"
#include "lluicolor.h"
+
+#if LL_DARWIN
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdelete-incomplete"
+#include "lluictrlfactory.h"
+#pragma clang diagnostic pop
+#else
#include "lluictrlfactory.h"
+#endif
+
+#include "boost/bind.hpp"
static LLInitParam::Parser::parser_read_func_map_t sReadFuncs;
static LLInitParam::Parser::parser_write_func_map_t sWriteFuncs;
diff --git a/indra/llui/llrngwriter.h b/indra/llui/llrngwriter.h
index c33aa28613..c33aa28613 100644..100755
--- a/indra/llui/llrngwriter.h
+++ b/indra/llui/llrngwriter.h
diff --git a/indra/llui/llscrollbar.cpp b/indra/llui/llscrollbar.cpp
index 3a867a10a7..76134144a0 100644..100755
--- a/indra/llui/llscrollbar.cpp
+++ b/indra/llui/llscrollbar.cpp
@@ -63,9 +63,7 @@ LLScrollbar::Params::Params()
right_button("right_button"),
bg_visible("bg_visible", false),
bg_color("bg_color", LLColor4::black)
-{
- tab_stop = false;
-}
+{}
LLScrollbar::LLScrollbar(const Params & p)
: LLUICtrl(p),
@@ -383,7 +381,7 @@ BOOL LLScrollbar::handleHover(S32 x, S32 y, MASK mask)
}
getWindow()->setCursor(UI_CURSOR_ARROW);
- lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (active)" << llendl;
+ LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (active)" << LL_ENDL;
handled = TRUE;
}
else
@@ -395,7 +393,7 @@ BOOL LLScrollbar::handleHover(S32 x, S32 y, MASK mask)
if( !handled )
{
getWindow()->setCursor(UI_CURSOR_ARROW);
- lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (inactive)" << llendl;
+ LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (inactive)" << LL_ENDL;
handled = TRUE;
}
@@ -495,11 +493,11 @@ void LLScrollbar::draw()
BOOL hovered = getEnabled() && !other_captor && (hasMouseCapture() || mThumbRect.pointInRect(local_mouse_x, local_mouse_y));
if (hovered)
{
- mCurGlowStrength = lerp(mCurGlowStrength, mHoverGlowStrength, LLCriticalDamp::getInterpolant(0.05f));
+ mCurGlowStrength = lerp(mCurGlowStrength, mHoverGlowStrength, LLSmoothInterpolation::getInterpolant(0.05f));
}
else
{
- mCurGlowStrength = lerp(mCurGlowStrength, 0.f, LLCriticalDamp::getInterpolant(0.05f));
+ mCurGlowStrength = lerp(mCurGlowStrength, 0.f, LLSmoothInterpolation::getInterpolant(0.05f));
}
// Draw background and thumb.
@@ -644,3 +642,8 @@ void LLScrollbar::onLineDownBtnPressed( const LLSD& data )
{
changeLine( mStepSize, TRUE );
}
+
+void LLScrollbar::setThickness(S32 thickness)
+{
+ mThickness = thickness < 0 ? LLUI::sSettingGroups["config"]->getS32("UIScrollbarSize") : thickness;
+}
diff --git a/indra/llui/llscrollbar.h b/indra/llui/llscrollbar.h
index ff74f753b9..e2bf52c14b 100644..100755
--- a/indra/llui/llscrollbar.h
+++ b/indra/llui/llscrollbar.h
@@ -40,13 +40,11 @@ class LLScrollbar
{
public:
- enum ORIENTATION { HORIZONTAL, VERTICAL };
-
typedef boost::function<void (S32, LLScrollbar*)> callback_t;
struct Params
: public LLInitParam::Block<Params, LLUICtrl::Params>
{
- Mandatory<ORIENTATION> orientation;
+ Mandatory<EOrientation> orientation;
Mandatory<S32> doc_size;
Mandatory<S32> doc_pos;
Mandatory<S32> page_size;
@@ -125,13 +123,16 @@ public:
void onLineUpBtnPressed(const LLSD& data);
void onLineDownBtnPressed(const LLSD& data);
+ S32 getThickness() const { return mThickness; }
+ void setThickness(S32 thickness);
+
private:
void updateThumbRect();
bool changeLine(S32 delta, BOOL update_thumb );
callback_t mChangeCallback;
- const ORIENTATION mOrientation;
+ const EOrientation mOrientation;
S32 mDocSize; // Size of the document that the scrollbar is modeling. Units depend on the user. 0 <= mDocSize.
S32 mDocPos; // Position within the doc that the scrollbar is modeling, in "lines" (user size)
S32 mPageSize; // Maximum number of lines that can be seen at one time.
diff --git a/indra/llui/llscrollcontainer.cpp b/indra/llui/llscrollcontainer.cpp
index 380c477eb2..f70eebc594 100644..100755
--- a/indra/llui/llscrollcontainer.cpp
+++ b/indra/llui/llscrollcontainer.cpp
@@ -48,7 +48,6 @@
/// Local function declarations, constants, enums, and typedefs
///----------------------------------------------------------------------------
-static const S32 HORIZONTAL_MULTIPLE = 8;
static const S32 VERTICAL_MULTIPLE = 16;
static const F32 AUTO_SCROLL_RATE_ACCEL = 120.f;
@@ -73,12 +72,9 @@ LLScrollContainer::Params::Params()
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";
- mouse_opaque(true);
- tab_stop(false);
-}
+ reserve_scroll_corner("reserve_scroll_corner", false),
+ size("size", -1)
+{}
// Default constructor
@@ -92,9 +88,12 @@ LLScrollContainer::LLScrollContainer(const LLScrollContainer::Params& p)
mReserveScrollCorner(p.reserve_scroll_corner),
mMinAutoScrollRate(p.min_auto_scroll_rate),
mMaxAutoScrollRate(p.max_auto_scroll_rate),
- mScrolledView(NULL)
+ mScrolledView(NULL),
+ mSize(p.size)
{
- static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
+ static LLUICachedControl<S32> scrollbar_size_control ("UIScrollbarSize", 0);
+ S32 scrollbar_size = (mSize == -1 ? scrollbar_size_control : mSize);
+
LLRect border_rect( 0, getRect().getHeight(), getRect().getWidth(), 0 );
LLViewBorder::Params params;
params.name("scroll border");
@@ -144,7 +143,7 @@ LLScrollContainer::~LLScrollContainer( void )
{
// mScrolledView and mScrollbar are child views, so the LLView
// destructor takes care of memory deallocation.
- for( S32 i = 0; i < SCROLLBAR_COUNT; i++ )
+ for( S32 i = 0; i < ORIENTATION_COUNT; i++ )
{
mScrollbar[i] = NULL;
}
@@ -155,7 +154,6 @@ LLScrollContainer::~LLScrollContainer( void )
// virtual
void LLScrollContainer::scrollHorizontal( S32 new_pos )
{
- //llinfos << "LLScrollContainer::scrollHorizontal()" << llendl;
if( mScrolledView )
{
LLRect doc_rect = mScrolledView->getRect();
@@ -167,7 +165,6 @@ void LLScrollContainer::scrollHorizontal( S32 new_pos )
// virtual
void LLScrollContainer::scrollVertical( S32 new_pos )
{
- // llinfos << "LLScrollContainer::scrollVertical() " << new_pos << llendl;
if( mScrolledView )
{
LLRect doc_rect = mScrolledView->getRect();
@@ -215,7 +212,7 @@ BOOL LLScrollContainer::handleKeyHere(KEY key, MASK mask)
{
return TRUE;
}
- for( S32 i = 0; i < SCROLLBAR_COUNT; i++ )
+ for( S32 i = 0; i < ORIENTATION_COUNT; i++ )
{
if( mScrollbar[i]->handleKeyHere(key, mask) )
{
@@ -227,6 +224,15 @@ BOOL LLScrollContainer::handleKeyHere(KEY key, MASK mask)
return FALSE;
}
+BOOL LLScrollContainer::handleUnicodeCharHere(llwchar uni_char)
+{
+ if (mScrolledView && mScrolledView->handleUnicodeCharHere(uni_char))
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
BOOL LLScrollContainer::handleScrollWheel( S32 x, S32 y, S32 clicks )
{
// Give event to my child views - they may have scroll bars
@@ -271,7 +277,6 @@ BOOL LLScrollContainer::handleDragAndDrop(S32 x, S32 y, MASK mask,
EAcceptance* accept,
std::string& tooltip_msg)
{
- static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
// Scroll folder view if needed. Never accepts a drag or drop.
*accept = ACCEPT_NO;
BOOL handled = autoScroll(x, y);
@@ -287,7 +292,8 @@ BOOL LLScrollContainer::handleDragAndDrop(S32 x, S32 y, MASK mask,
bool LLScrollContainer::autoScroll(S32 x, S32 y)
{
- static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
+ static LLUICachedControl<S32> scrollbar_size_control ("UIScrollbarSize", 0);
+ S32 scrollbar_size = (mSize == -1 ? scrollbar_size_control : mSize);
bool scrolling = false;
if( mScrollbar[HORIZONTAL]->getVisible() || mScrollbar[VERTICAL]->getVisible() )
@@ -308,7 +314,7 @@ bool LLScrollContainer::autoScroll(S32 x, S32 y)
// clip rect against root view
inner_rect_local.intersectWith(screen_local_extents);
- S32 auto_scroll_speed = llround(mAutoScrollRate * LLFrameTimer::getFrameDeltaTimeF32());
+ S32 auto_scroll_speed = ll_round(mAutoScrollRate * LLFrameTimer::getFrameDeltaTimeF32());
// autoscroll region should take up no more than one third of visible scroller area
S32 auto_scroll_region_width = llmin(inner_rect_local.getWidth() / 3, 10);
S32 auto_scroll_region_height = llmin(inner_rect_local.getHeight() / 3, 10);
@@ -360,7 +366,9 @@ bool LLScrollContainer::autoScroll(S32 x, S32 y)
void LLScrollContainer::calcVisibleSize( S32 *visible_width, S32 *visible_height, BOOL* show_h_scrollbar, BOOL* show_v_scrollbar ) const
{
const LLRect& doc_rect = getScrolledViewRect();
- static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
+ static LLUICachedControl<S32> scrollbar_size_control ("UIScrollbarSize", 0);
+ S32 scrollbar_size = (mSize == -1 ? scrollbar_size_control : mSize);
+
S32 doc_width = doc_rect.getWidth();
S32 doc_height = doc_rect.getHeight();
@@ -373,19 +381,23 @@ void LLScrollContainer::calcVisibleSize( S32 *visible_width, S32 *visible_height
if (!mHideScrollbar)
{
- if( *visible_height < doc_height )
+ // Note: 1 pixel change can happen on final animation and should not trigger
+ // the display of sliders.
+ if ((doc_height - *visible_height) > 1)
{
*show_v_scrollbar = TRUE;
*visible_width -= scrollbar_size;
}
-
- if( *visible_width < doc_width )
+ if ((doc_width - *visible_width) > 1)
{
*show_h_scrollbar = TRUE;
*visible_height -= scrollbar_size;
+ // Note: Do *not* recompute *show_v_scrollbar here because with
+ // The view inside the scroll container should not be extended
+ // to container's full height to ensure the correct computation
+ // of *show_v_scrollbar after subtracting horizontal scrollbar_size.
- // Must retest now that visible_height has changed
- if( !*show_v_scrollbar && (*visible_height < doc_height) )
+ if( !*show_v_scrollbar && ((doc_height - *visible_height) > 1) )
{
*show_v_scrollbar = TRUE;
*visible_width -= scrollbar_size;
@@ -397,7 +409,9 @@ void LLScrollContainer::calcVisibleSize( S32 *visible_width, S32 *visible_height
void LLScrollContainer::draw()
{
- static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
+ static LLUICachedControl<S32> scrollbar_size_control ("UIScrollbarSize", 0);
+ S32 scrollbar_size = (mSize == -1 ? scrollbar_size_control : mSize);
+
if (mAutoScrolling)
{
// add acceleration to autoscroll
@@ -419,63 +433,66 @@ void LLScrollContainer::draw()
focusFirstItem();
}
- // Draw background
- if( mIsOpaque )
+ if (getRect().isValid())
{
- F32 alpha = getCurrentTransparency();
+ // Draw background
+ if( mIsOpaque )
+ {
+ F32 alpha = getCurrentTransparency();
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
- gl_rect_2d(mInnerRect, mBackgroundColor.get() % alpha);
- }
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+ gl_rect_2d(mInnerRect, mBackgroundColor.get() % alpha);
+ }
- // Draw mScrolledViews and update scroll bars.
- // get a scissor region ready, and draw the scrolling view. The
- // scissor region ensures that we don't draw outside of the bounds
- // of the rectangle.
- if( mScrolledView )
- {
- updateScroll();
-
- // Draw the scrolled area.
+ // Draw mScrolledViews and update scroll bars.
+ // get a scissor region ready, and draw the scrolling view. The
+ // scissor region ensures that we don't draw outside of the bounds
+ // of the rectangle.
+ if( mScrolledView )
{
- S32 visible_width = 0;
- S32 visible_height = 0;
- BOOL show_v_scrollbar = FALSE;
- BOOL show_h_scrollbar = FALSE;
- calcVisibleSize( &visible_width, &visible_height, &show_h_scrollbar, &show_v_scrollbar );
-
- LLLocalClipRect clip(LLRect(mInnerRect.mLeft,
- mInnerRect.mBottom + (show_h_scrollbar ? scrollbar_size : 0) + visible_height,
- mInnerRect.mRight - (show_v_scrollbar ? scrollbar_size: 0),
- mInnerRect.mBottom + (show_h_scrollbar ? scrollbar_size : 0)
- ));
- drawChild(mScrolledView);
- }
- }
-
- // Highlight border if a child of this container has keyboard focus
- if( mBorder->getVisible() )
- {
- mBorder->setKeyboardFocusHighlight( gFocusMgr.childHasKeyboardFocus(this) );
- }
+ updateScroll();
- // Draw all children except mScrolledView
- // Note: scrollbars have been adjusted by above drawing code
- for (child_list_const_reverse_iter_t child_iter = getChildList()->rbegin();
- child_iter != getChildList()->rend(); ++child_iter)
- {
- LLView *viewp = *child_iter;
- if( sDebugRects )
- {
- sDepth++;
+ // Draw the scrolled area.
+ {
+ S32 visible_width = 0;
+ S32 visible_height = 0;
+ BOOL show_v_scrollbar = FALSE;
+ BOOL show_h_scrollbar = FALSE;
+ calcVisibleSize( &visible_width, &visible_height, &show_h_scrollbar, &show_v_scrollbar );
+
+ LLLocalClipRect clip(LLRect(mInnerRect.mLeft,
+ mInnerRect.mBottom + (show_h_scrollbar ? scrollbar_size : 0) + visible_height,
+ mInnerRect.mRight - (show_v_scrollbar ? scrollbar_size: 0),
+ mInnerRect.mBottom + (show_h_scrollbar ? scrollbar_size : 0)
+ ));
+ drawChild(mScrolledView);
+ }
}
- if( (viewp != mScrolledView) && viewp->getVisible() )
+
+ // Highlight border if a child of this container has keyboard focus
+ if( mBorder->getVisible() )
{
- drawChild(viewp);
+ mBorder->setKeyboardFocusHighlight( gFocusMgr.childHasKeyboardFocus(this) );
}
- if( sDebugRects )
+
+ // Draw all children except mScrolledView
+ // Note: scrollbars have been adjusted by above drawing code
+ for (child_list_const_reverse_iter_t child_iter = getChildList()->rbegin();
+ child_iter != getChildList()->rend(); ++child_iter)
{
- sDepth--;
+ LLView *viewp = *child_iter;
+ if( sDebugRects )
+ {
+ sDepth++;
+ }
+ if( (viewp != mScrolledView) && viewp->getVisible() )
+ {
+ drawChild(viewp);
+ }
+ if( sDebugRects )
+ {
+ sDepth--;
+ }
}
}
} // end draw
@@ -499,11 +516,13 @@ bool LLScrollContainer::addChild(LLView* view, S32 tab_group)
void LLScrollContainer::updateScroll()
{
- if (!mScrolledView)
+ if (!getVisible() || !mScrolledView)
{
return;
}
- static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
+ static LLUICachedControl<S32> scrollbar_size_control ("UIScrollbarSize", 0);
+ S32 scrollbar_size = (mSize == -1 ? scrollbar_size_control : mSize);
+
LLRect doc_rect = mScrolledView->getRect();
S32 doc_width = doc_rect.getWidth();
S32 doc_height = doc_rect.getHeight();
@@ -623,7 +642,7 @@ void LLScrollContainer::scrollToShowRect(const LLRect& rect, const LLRect& const
{
if (!mScrolledView)
{
- llwarns << "LLScrollContainer::scrollToShowRect with no view!" << llendl;
+ LL_WARNS() << "LLScrollContainer::scrollToShowRect with no view!" << LL_ENDL;
return;
}
@@ -704,3 +723,9 @@ S32 LLScrollContainer::getBorderWidth() const
return 0;
}
+void LLScrollContainer::setSize(S32 size)
+{
+ mSize = size;
+ mScrollbar[VERTICAL]->setThickness(size);
+ mScrollbar[HORIZONTAL]->setThickness(size);
+}
diff --git a/indra/llui/llscrollcontainer.h b/indra/llui/llscrollcontainer.h
index 46a71a7e30..f64cf43a8e 100644..100755
--- a/indra/llui/llscrollcontainer.h
+++ b/indra/llui/llscrollcontainer.h
@@ -31,7 +31,6 @@
#ifndef LL_V4COLOR_H
#include "v4color.h"
#endif
-#include "stdenums.h"
#include "llcoord.h"
#include "llscrollbar.h"
@@ -56,7 +55,6 @@ class LLScrollContainer : public LLUICtrl
public:
// Note: vertical comes before horizontal because vertical
// scrollbars have priority for mouse and keyboard events.
- enum SCROLL_ORIENTATION { VERTICAL, HORIZONTAL, SCROLLBAR_COUNT };
struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
{
@@ -68,6 +66,7 @@ public:
max_auto_scroll_rate;
Optional<LLUIColor> bg_color;
Optional<LLScrollbar::callback_t> scroll_callback;
+ Optional<S32> size;
Params();
};
@@ -91,7 +90,7 @@ public:
void setReserveScrollCorner( BOOL b ) { mReserveScrollCorner = b; }
LLRect getVisibleContentRect();
LLRect getContentWindowRect();
- const LLRect& getScrolledViewRect() const { return mScrolledView ? mScrolledView->getRect() : LLRect::null; }
+ virtual const LLRect getScrolledViewRect() const { return mScrolledView ? mScrolledView->getRect() : LLRect::null; }
void pageUp(S32 overlap = 0);
void pageDown(S32 overlap = 0);
void goToTop();
@@ -103,6 +102,7 @@ public:
// LLView functionality
virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
virtual BOOL handleKeyHere(KEY key, MASK mask);
+ virtual BOOL handleUnicodeCharHere(llwchar uni_char);
virtual BOOL handleScrollWheel( S32 x, S32 y, S32 clicks );
virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
EDragAndDropType cargo_type,
@@ -115,6 +115,12 @@ public:
bool autoScroll(S32 x, S32 y);
+ S32 getSize() const { return mSize; }
+ void setSize(S32 thickness);
+
+protected:
+ LLView* mScrolledView;
+
private:
// internal scrollbar handlers
virtual void scrollHorizontal( S32 new_pos );
@@ -122,8 +128,7 @@ private:
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;
+ LLScrollbar* mScrollbar[ORIENTATION_COUNT];
S32 mSize;
BOOL mIsOpaque;
LLUIColor mBackgroundColor;
diff --git a/indra/llui/llscrollingpanellist.cpp b/indra/llui/llscrollingpanellist.cpp
index 9b65c2b79d..b6f2eb8ba2 100644..100755
--- a/indra/llui/llscrollingpanellist.cpp
+++ b/indra/llui/llscrollingpanellist.cpp
@@ -111,7 +111,7 @@ void LLScrollingPanelList::removePanel( U32 panel_index )
{
if ( mPanelList.empty() || panel_index >= mPanelList.size() )
{
- llwarns << "Panel index " << panel_index << " is out of range!" << llendl;
+ LL_WARNS() << "Panel index " << panel_index << " is out of range!" << LL_ENDL;
return;
}
else
diff --git a/indra/llui/llscrollingpanellist.h b/indra/llui/llscrollingpanellist.h
index 8f569c2a58..e8df176ec3 100644..100755
--- a/indra/llui/llscrollingpanellist.h
+++ b/indra/llui/llscrollingpanellist.h
@@ -51,12 +51,7 @@ class LLScrollingPanelList : public LLUICtrl
{
public:
struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
- {
- Params()
- {
- name = "scrolling_panel_list";
- }
- };
+ {};
LLScrollingPanelList(const Params& p)
: LLUICtrl(p)
{}
diff --git a/indra/llui/llscrolllistcell.cpp b/indra/llui/llscrolllistcell.cpp
index 9d25c7180d..8000efad0e 100644..100755
--- a/indra/llui/llscrolllistcell.cpp
+++ b/indra/llui/llscrolllistcell.cpp
@@ -232,7 +232,7 @@ BOOL LLScrollListText::getVisible() const
//virtual
S32 LLScrollListText::getHeight() const
{
- return llround(mFont->getLineHeight());
+ return mFont->getLineHeight();
}
@@ -306,7 +306,7 @@ void LLScrollListText::draw(const LLColor4& color, const LLColor4& highlight_col
break;
}
LLRect highlight_rect(left - 2,
- llround(mFont->getLineHeight()) + 1,
+ mFont->getLineHeight() + 1,
left + mFont->getWidth(mText.getString(), mHighlightOffset, mHighlightCount) + 1,
1);
mRoundedRectImage->draw(highlight_rect, highlight_color);
@@ -329,7 +329,7 @@ void LLScrollListText::draw(const LLColor4& color, const LLColor4& highlight_col
break;
}
mFont->render(mText.getWString(), 0,
- start_x, 2.f,
+ start_x, 0.f,
display_color,
mFontAlignment,
LLFontGL::BOTTOM,
diff --git a/indra/llui/llscrolllistcell.h b/indra/llui/llscrolllistcell.h
index d625ebddcc..d625ebddcc 100644..100755
--- a/indra/llui/llscrolllistcell.h
+++ b/indra/llui/llscrolllistcell.h
diff --git a/indra/llui/llscrolllistcolumn.cpp b/indra/llui/llscrolllistcolumn.cpp
index 696e4a2bb1..cc9ff7a487 100644..100755
--- a/indra/llui/llscrolllistcolumn.cpp
+++ b/indra/llui/llscrolllistcolumn.cpp
@@ -46,10 +46,7 @@ static LLWidgetNameRegistry::StaticRegistrar sRegisterColumnHeaderParams(&typeid
//---------------------------------------------------------------------------
LLScrollColumnHeader::Params::Params()
: column("column")
-{
- name = "column_header";
- tab_stop(false);
-}
+{}
LLScrollColumnHeader::LLScrollColumnHeader(const LLScrollColumnHeader::Params& p)
@@ -101,6 +98,7 @@ BOOL LLScrollColumnHeader::handleDoubleClick(S32 x, S32 y, MASK mask)
if (canResize() && mResizeBar->getRect().pointInRect(x, y))
{
// reshape column to max content width
+ mColumn->mParentCtrl->calcMaxContentWidth();
LLRect column_rect = getRect();
column_rect.mRight = column_rect.mLeft + mColumn->mMaxContentWidth;
setShape(column_rect, true);
@@ -130,6 +128,8 @@ LLView* LLScrollColumnHeader::findSnapEdge(S32& new_edge_val, const LLCoordGL& m
LLRect snap_rect = getSnapRect();
+ mColumn->mParentCtrl->calcMaxContentWidth();
+
S32 snap_delta = mColumn->mMaxContentWidth - snap_rect.getWidth();
// x coord growing means column growing, so same signs mean we're going in right direction
@@ -236,7 +236,8 @@ void LLScrollColumnHeader::handleReshape(const LLRect& new_rect, bool by_user)
// tell scroll list to layout columns again
// do immediate update to get proper feedback to resize handle
// which needs to know how far the resize actually went
- mColumn->mParentCtrl->updateColumns();
+ const bool force_update = true;
+ mColumn->mParentCtrl->updateColumns(force_update);
}
}
diff --git a/indra/llui/llscrolllistcolumn.h b/indra/llui/llscrolllistcolumn.h
index e2711ac75a..b4d4a6d05e 100644..100755
--- a/indra/llui/llscrolllistcolumn.h
+++ b/indra/llui/llscrolllistcolumn.h
@@ -95,7 +95,7 @@ public:
Optional<ESortDirection, SortNames> sort_direction;
Optional<bool> sort_ascending;
- struct Width : public LLInitParam::Choice<Width>
+ struct Width : public LLInitParam::ChoiceBlock<Width>
{
Alternative<bool> dynamic_width;
Alternative<S32> pixel_width;
@@ -112,7 +112,7 @@ public:
Optional<Width> width;
// either an image or label is used in column header
- struct Header : public LLInitParam::Choice<Header>
+ struct Header : public LLInitParam::ChoiceBlock<Header>
{
Alternative<std::string> label;
Alternative<LLUIImage*> image;
@@ -135,7 +135,7 @@ public:
halign("halign", LLFontGL::LEFT)
{
// default choice to "dynamic_width"
- width.dynamic_width = true;
+ changeDefault(width.dynamic_width, true);
addSynonym(sort_column, "sort");
}
diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp
index b7848ec37c..db8fdc46b7 100644..100755
--- a/indra/llui/llscrolllistctrl.cpp
+++ b/indra/llui/llscrolllistctrl.cpp
@@ -35,6 +35,7 @@
#include "llboost.h"
//#include "indra_constants.h"
+#include "llavatarnamecache.h"
#include "llcheckboxctrl.h"
#include "llclipboard.h"
#include "llfocusmgr.h"
@@ -147,12 +148,9 @@ LLScrollListCtrl::Params::Params()
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;
-}
+ scroll_bar_bg_color("scroll_bar_bg_color"),
+ border("border")
+{}
LLScrollListCtrl::LLScrollListCtrl(const LLScrollListCtrl::Params& p)
: LLUICtrl(p),
@@ -161,15 +159,14 @@ LLScrollListCtrl::LLScrollListCtrl(const LLScrollListCtrl::Params& p)
mMouseWheelOpaque(p.mouse_wheel_opaque),
mPageLines(p.page_lines),
mMaxSelectable(0),
- mAllowKeyboardMovement(TRUE),
+ mAllowKeyboardMovement(true),
mCommitOnKeyboardMovement(p.commit_on_keyboard_movement),
- mCommitOnSelectionChange(FALSE),
- mSelectionChanged(FALSE),
- mNeedsScroll(FALSE),
- mCanSelect(TRUE),
- mColumnsDirty(FALSE),
+ mCommitOnSelectionChange(false),
+ mSelectionChanged(false),
+ mNeedsScroll(false),
+ mCanSelect(true),
+ mColumnsDirty(false),
mMaxItemCount(INT_MAX),
- mMaxContentWidth(0),
mBorderThickness( 2 ),
mOnDoubleClickCallback( NULL ),
mOnMaximumSelectCallback( NULL ),
@@ -178,11 +175,12 @@ LLScrollListCtrl::LLScrollListCtrl(const LLScrollListCtrl::Params& p)
mBorder(NULL),
mSortCallback(NULL),
mPopupMenu(NULL),
+ mCommentTextView(NULL),
mNumDynamicWidthColumns(0),
mTotalStaticColumnWidth(0),
mTotalColumnPadding(0),
mSorted(false),
- mDirty(FALSE),
+ mDirty(false),
mOriginalSelection(-1),
mLastSelected(NULL),
mHeadingHeight(p.heading_height),
@@ -322,7 +320,9 @@ LLScrollListCtrl::~LLScrollListCtrl()
delete mSortCallback;
std::for_each(mItemList.begin(), mItemList.end(), DeletePointer());
+ mItemList.clear();
std::for_each(mColumns.begin(), mColumns.end(), DeletePairedPointer());
+ mColumns.clear();
}
@@ -358,7 +358,7 @@ void LLScrollListCtrl::clearRows()
mScrollLines = 0;
mLastSelected = NULL;
updateLayout();
- mDirty = FALSE;
+ mDirty = false;
}
@@ -391,6 +391,22 @@ std::vector<LLScrollListItem*> LLScrollListCtrl::getAllSelected() const
return ret;
}
+S32 LLScrollListCtrl::getNumSelected() const
+{
+ S32 numSelected = 0;
+
+ for(item_list::const_iterator iter = mItemList.begin(); iter != mItemList.end(); ++iter)
+ {
+ LLScrollListItem* item = *iter;
+ if (item->getSelected())
+ {
+ ++numSelected;
+ }
+ }
+
+ return numSelected;
+}
+
S32 LLScrollListCtrl::getFirstSelectedIndex() const
{
S32 CurSelectedIndex = 0;
@@ -479,7 +495,12 @@ void LLScrollListCtrl::updateLayout()
getRect().getWidth() - 2 * mBorderThickness,
getRect().getHeight() - (2 * mBorderThickness ) - heading_size );
- getChildView("comment_text")->setShape(mItemListRect);
+ if (mCommentTextView == NULL)
+ {
+ mCommentTextView = getChildView("comment_text");
+ }
+
+ mCommentTextView->setShape(mItemListRect);
// how many lines of content in a single "page"
S32 page_lines = getLinesPerPage();
@@ -561,6 +582,15 @@ BOOL LLScrollListCtrl::addItem( LLScrollListItem* item, EAddPosition pos, BOOL r
addColumn(col_params);
}
+ S32 num_cols = item->getNumColumns();
+ S32 i = 0;
+ for (LLScrollListCell* cell = item->getColumn(i); i < num_cols; cell = item->getColumn(++i))
+ {
+ if (i >= (S32)mColumnsIndexed.size()) break;
+
+ cell->setWidth(mColumnsIndexed[i]->getWidth());
+ }
+
updateLineHeightInsert(item);
updateLayout();
@@ -572,13 +602,11 @@ BOOL LLScrollListCtrl::addItem( LLScrollListItem* item, EAddPosition pos, BOOL r
// NOTE: This is *very* expensive for large lists, especially when we are dirtying the list every frame
// while receiving a long list of names.
// *TODO: Use bookkeeping to make this an incramental cost with item additions
-void LLScrollListCtrl::calcColumnWidths()
+S32 LLScrollListCtrl::calcMaxContentWidth()
{
const S32 HEADING_TEXT_PADDING = 25;
const S32 COLUMN_TEXT_PADDING = 10;
- mMaxContentWidth = 0;
-
S32 max_item_width = 0;
ordered_columns_t::iterator column_itor;
@@ -587,34 +615,57 @@ void LLScrollListCtrl::calcColumnWidths()
LLScrollListColumn* column = *column_itor;
if (!column) continue;
+ if (mColumnWidthsDirty)
+ {
+ // update max content width for this column, by looking at all items
+ column->mMaxContentWidth = column->mHeader ? LLFontGL::getFontSansSerifSmall()->getWidth(column->mLabel) + mColumnPadding + HEADING_TEXT_PADDING : 0;
+ item_list::iterator iter;
+ for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
+ {
+ LLScrollListCell* cellp = (*iter)->getColumn(column->mIndex);
+ if (!cellp) continue;
+
+ column->mMaxContentWidth = llmax(LLFontGL::getFontSansSerifSmall()->getWidth(cellp->getValue().asString()) + mColumnPadding + COLUMN_TEXT_PADDING, column->mMaxContentWidth);
+ }
+ }
+ max_item_width += column->mMaxContentWidth;
+ }
+ mColumnWidthsDirty = false;
+
+ return max_item_width;
+}
+
+bool LLScrollListCtrl::updateColumnWidths()
+{
+ bool width_changed = false;
+ ordered_columns_t::iterator column_itor;
+ for (column_itor = mColumnsIndexed.begin(); column_itor != mColumnsIndexed.end(); ++column_itor)
+ {
+ LLScrollListColumn* column = *column_itor;
+ if (!column) continue;
+
// update column width
- S32 new_width = column->getWidth();
+ S32 new_width = 0;
if (column->mRelWidth >= 0)
{
- new_width = (S32)llround(column->mRelWidth*mItemListRect.getWidth());
+ new_width = (S32)ll_round(column->mRelWidth*mItemListRect.getWidth());
}
else if (column->mDynamicWidth)
{
new_width = (mItemListRect.getWidth() - mTotalStaticColumnWidth - mTotalColumnPadding) / mNumDynamicWidthColumns;
}
-
- column->setWidth(new_width);
-
- // update max content width for this column, by looking at all items
- column->mMaxContentWidth = column->mHeader ? LLFontGL::getFontSansSerifSmall()->getWidth(column->mLabel) + mColumnPadding + HEADING_TEXT_PADDING : 0;
- item_list::iterator iter;
- for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
+ else
{
- LLScrollListCell* cellp = (*iter)->getColumn(column->mIndex);
- if (!cellp) continue;
-
- column->mMaxContentWidth = llmax(LLFontGL::getFontSansSerifSmall()->getWidth(cellp->getValue().asString()) + mColumnPadding + COLUMN_TEXT_PADDING, column->mMaxContentWidth);
+ new_width = column->getWidth();
}
- max_item_width += column->mMaxContentWidth;
+ if (column->getWidth() != new_width)
+ {
+ column->setWidth(new_width);
+ width_changed = true;
+ }
}
-
- mMaxContentWidth = max_item_width;
+ return width_changed;
}
const S32 SCROLL_LIST_ROW_PAD = 2;
@@ -648,9 +699,14 @@ void LLScrollListCtrl::updateLineHeightInsert(LLScrollListItem* itemp)
}
-void LLScrollListCtrl::updateColumns()
+void LLScrollListCtrl::updateColumns(bool force_update)
{
- calcColumnWidths();
+ if (!mColumnsDirty && !force_update)
+ return;
+
+ mColumnsDirty = false;
+
+ bool columns_changed_width = updateColumnWidths();
// update column headers
std::vector<LLScrollListColumn*>::iterator column_ordered_it;
@@ -699,20 +755,22 @@ void LLScrollListCtrl::updateColumns()
}
// propagate column widths to individual cells
- item_list::iterator iter;
- for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
+ if (columns_changed_width || force_update)
{
- LLScrollListItem *itemp = *iter;
- S32 num_cols = itemp->getNumColumns();
- S32 i = 0;
- for (LLScrollListCell* cell = itemp->getColumn(i); i < num_cols; cell = itemp->getColumn(++i))
+ item_list::iterator iter;
+ for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
{
- if (i >= (S32)mColumnsIndexed.size()) break;
+ LLScrollListItem *itemp = *iter;
+ S32 num_cols = itemp->getNumColumns();
+ S32 i = 0;
+ for (LLScrollListCell* cell = itemp->getColumn(i); i < num_cols; cell = itemp->getColumn(++i))
+ {
+ if (i >= (S32)mColumnsIndexed.size()) break;
- cell->setWidth(mColumnsIndexed[i]->getWidth());
+ cell->setWidth(mColumnsIndexed[i]->getWidth());
+ }
}
}
-
}
void LLScrollListCtrl::setHeadingHeight(S32 heading_height)
@@ -753,7 +811,7 @@ BOOL LLScrollListCtrl::selectFirstItem()
{
deselectItem(itemp);
}
- first_item = FALSE;
+ first_item = false;
}
if (mCommitOnSelectionChange)
{
@@ -1127,10 +1185,10 @@ LLScrollListItem* LLScrollListCtrl::addSeparator(EAddPosition pos)
// Selects first enabled item of the given name.
// Returns false if item not found.
// Calls getItemByLabel in order to combine functionality
-BOOL LLScrollListCtrl::selectItemByLabel(const std::string& label, BOOL case_sensitive)
+BOOL LLScrollListCtrl::selectItemByLabel(const std::string& label, BOOL case_sensitive, S32 column/* = 0*/)
{
deselectAllItems(TRUE); // ensure that no stale items are selected, even if we don't find a match
- LLScrollListItem* item = getItemByLabel(label, case_sensitive);
+ LLScrollListItem* item = getItemByLabel(label, case_sensitive, column);
bool found = NULL != item;
if(found)
@@ -1387,17 +1445,23 @@ void LLScrollListCtrl::drawItems()
S32 cur_y = y;
- S32 line = 0;
S32 max_columns = 0;
LLColor4 highlight_color = LLColor4::white;
static LLUICachedControl<F32> type_ahead_timeout ("TypeAheadTimeout", 0);
- highlight_color.mV[VALPHA] = clamp_rescale(mSearchTimer.getElapsedTimeF32(), type_ahead_timeout * 0.7f, type_ahead_timeout, 0.4f, 0.f);
+ highlight_color.mV[VALPHA] = clamp_rescale(mSearchTimer.getElapsedTimeF32(), type_ahead_timeout * 0.7f, type_ahead_timeout(), 0.4f, 0.f);
+ S32 first_line = mScrollLines;
+ S32 last_line = llmin((S32)mItemList.size() - 1, mScrollLines + getLinesPerPage());
+
+ if (first_line >= mItemList.size())
+ {
+ return;
+ }
item_list::iterator iter;
- for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
+ for (S32 line = first_line; line <= last_line; line++)
{
- LLScrollListItem* item = *iter;
+ LLScrollListItem* item = mItemList[line];
item_rect.setOriginAndSize(
x,
@@ -1406,8 +1470,6 @@ void LLScrollListCtrl::drawItems()
mLineHeight );
item->setRect(item_rect);
- //llinfos << item_rect.getWidth() << llendl;
-
max_columns = llmax(max_columns, item->getNumColumns());
LLColor4 fg_color;
@@ -1461,7 +1523,6 @@ void LLScrollListCtrl::drawItems()
cur_y -= mLineHeight;
}
- line++;
}
}
}
@@ -1477,7 +1538,7 @@ void LLScrollListCtrl::draw()
if (mNeedsScroll)
{
scrollToShowSelected();
- mNeedsScroll = FALSE;
+ mNeedsScroll = false;
}
LLRect background(0, getRect().getHeight(), getRect().getWidth(), 0);
// Draw background
@@ -1488,11 +1549,7 @@ void LLScrollListCtrl::draw()
gl_rect_2d(background, getEnabled() ? mBgWriteableColor.get() % alpha : mBgReadOnlyColor.get() % alpha );
}
- if (mColumnsDirty)
- {
- updateColumns();
- mColumnsDirty = FALSE;
- }
+ updateColumns();
getChildView("comment_text")->setVisible(mItemList.empty());
@@ -1700,7 +1757,7 @@ BOOL LLScrollListCtrl::handleMouseDown(S32 x, S32 y, MASK mask)
setFocus(TRUE);
// clear selection changed flag because user is starting a selection operation
- mSelectionChanged = FALSE;
+ mSelectionChanged = false;
handleClick(x, y, mask);
}
@@ -1718,15 +1775,15 @@ BOOL LLScrollListCtrl::handleMouseUp(S32 x, S32 y, MASK mask)
if(mask == MASK_NONE)
{
selectItemAt(x, y, mask);
- mNeedsScroll = TRUE;
+ mNeedsScroll = true;
}
}
// always commit when mouse operation is completed inside list
if (mItemListRect.pointInRect(x,y))
{
- mDirty |= mSelectionChanged;
- mSelectionChanged = FALSE;
+ mDirty = mDirty || mSelectionChanged;
+ mSelectionChanged = false;
onCommit();
}
@@ -1748,6 +1805,9 @@ BOOL LLScrollListCtrl::handleRightMouseDown(S32 x, S32 y, MASK mask)
// (N.B. callbacks don't take const refs as id is local scope)
bool is_group = (mContextMenuType == MENU_GROUP);
LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
+ registrar.add("Url.ShowProfile", boost::bind(&LLScrollListCtrl::showProfile, id, is_group));
+ registrar.add("Url.SendIM", boost::bind(&LLScrollListCtrl::sendIM, id));
+ registrar.add("Url.AddFriend", boost::bind(&LLScrollListCtrl::addFriend, id));
registrar.add("Url.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));
@@ -1755,6 +1815,7 @@ BOOL LLScrollListCtrl::handleRightMouseDown(S32 x, S32 y, MASK mask)
// 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;
+ llassert(LLMenuGL::sMenuContainer != NULL);
mPopupMenu = LLUICtrlFactory::getInstance()->createFromFile<LLContextMenu>(
menu_name, LLMenuGL::sMenuContainer, LLMenuHolderGL::child_registry_t::instance());
if (mPopupMenu)
@@ -1764,16 +1825,39 @@ BOOL LLScrollListCtrl::handleRightMouseDown(S32 x, S32 y, MASK mask)
return TRUE;
}
}
+ return LLUICtrl::handleRightMouseDown(x, y, mask);
}
return FALSE;
}
-void LLScrollListCtrl::showNameDetails(std::string id, bool is_group)
+void LLScrollListCtrl::showProfile(std::string id, bool is_group)
{
// show the resident's profile or the group profile
std::string sltype = is_group ? "group" : "agent";
std::string slurl = "secondlife:///app/" + sltype + "/" + id + "/about";
- LLUrlAction::clickAction(slurl);
+ LLUrlAction::showProfile(slurl);
+}
+
+void LLScrollListCtrl::sendIM(std::string id)
+{
+ // send im to the resident
+ std::string slurl = "secondlife:///app/agent/" + id + "/about";
+ LLUrlAction::sendIM(slurl);
+}
+
+void LLScrollListCtrl::addFriend(std::string id)
+{
+ // add resident to friends list
+ std::string slurl = "secondlife:///app/agent/" + id + "/about";
+ LLUrlAction::addFriend(slurl);
+}
+
+void LLScrollListCtrl::showNameDetails(std::string id, bool is_group)
+{
+ // open the resident's details or the group details
+ std::string sltype = is_group ? "group" : "agent";
+ std::string slurl = "secondlife:///app/" + sltype + "/" + id + "/about";
+ LLUrlAction::clickAction(slurl, true);
}
void LLScrollListCtrl::copyNameToClipboard(std::string id, bool is_group)
@@ -1786,7 +1870,9 @@ void LLScrollListCtrl::copyNameToClipboard(std::string id, bool is_group)
}
else
{
- gCacheName->getFullName(LLUUID(id), name);
+ LLAvatarName av_name;
+ LLAvatarNameCache::get(LLUUID(id), &av_name);
+ name = av_name.getAccountName();
}
LLUrlAction::copyURLToClipboard(name);
}
@@ -1840,7 +1926,7 @@ BOOL LLScrollListCtrl::handleClick(S32 x, S32 y, MASK mask)
{
selectItemAt(x, y, mask);
gFocusMgr.setMouseCapture(this);
- mNeedsScroll = TRUE;
+ mNeedsScroll = true;
}
// propagate state of cell to rest of selected column
@@ -1869,7 +1955,7 @@ BOOL LLScrollListCtrl::handleClick(S32 x, S32 y, MASK mask)
// treat this as a normal single item selection
selectItemAt(x, y, mask);
gFocusMgr.setMouseCapture(this);
- mNeedsScroll = TRUE;
+ mNeedsScroll = true;
// do not eat click (allow double click callback)
return FALSE;
}
@@ -1975,7 +2061,7 @@ BOOL LLScrollListCtrl::handleHover(S32 x,S32 y,MASK mask)
if(mask == MASK_NONE)
{
selectItemAt(x, y, mask);
- mNeedsScroll = TRUE;
+ mNeedsScroll = true;
}
}
else
@@ -2010,9 +2096,6 @@ BOOL LLScrollListCtrl::handleKeyHere(KEY key,MASK mask )
// not called from parent means we have keyboard focus or a child does
if (mCanSelect)
{
- // Ignore capslock
- mask = mask;
-
if (mask == MASK_NONE)
{
switch(key)
@@ -2022,7 +2105,7 @@ BOOL LLScrollListCtrl::handleKeyHere(KEY key,MASK mask )
{
// commit implicit in call
selectPrevItem(FALSE);
- mNeedsScroll = TRUE;
+ mNeedsScroll = true;
handled = TRUE;
}
break;
@@ -2031,7 +2114,7 @@ BOOL LLScrollListCtrl::handleKeyHere(KEY key,MASK mask )
{
// commit implicit in call
selectNextItem(FALSE);
- mNeedsScroll = TRUE;
+ mNeedsScroll = true;
handled = TRUE;
}
break;
@@ -2039,7 +2122,7 @@ BOOL LLScrollListCtrl::handleKeyHere(KEY key,MASK mask )
if (mAllowKeyboardMovement || hasFocus())
{
selectNthItem(getFirstSelectedIndex() - (mScrollbar->getPageSize() - 1));
- mNeedsScroll = TRUE;
+ mNeedsScroll = true;
if (mCommitOnKeyboardMovement
&& !mCommitOnSelectionChange)
{
@@ -2052,7 +2135,7 @@ BOOL LLScrollListCtrl::handleKeyHere(KEY key,MASK mask )
if (mAllowKeyboardMovement || hasFocus())
{
selectNthItem(getFirstSelectedIndex() + (mScrollbar->getPageSize() - 1));
- mNeedsScroll = TRUE;
+ mNeedsScroll = true;
if (mCommitOnKeyboardMovement
&& !mCommitOnSelectionChange)
{
@@ -2065,7 +2148,7 @@ BOOL LLScrollListCtrl::handleKeyHere(KEY key,MASK mask )
if (mAllowKeyboardMovement || hasFocus())
{
selectFirstItem();
- mNeedsScroll = TRUE;
+ mNeedsScroll = true;
if (mCommitOnKeyboardMovement
&& !mCommitOnSelectionChange)
{
@@ -2078,7 +2161,7 @@ BOOL LLScrollListCtrl::handleKeyHere(KEY key,MASK mask )
if (mAllowKeyboardMovement || hasFocus())
{
selectNthItem(getItemCount() - 1);
- mNeedsScroll = TRUE;
+ mNeedsScroll = true;
if (mCommitOnKeyboardMovement
&& !mCommitOnSelectionChange)
{
@@ -2117,7 +2200,7 @@ BOOL LLScrollListCtrl::handleKeyHere(KEY key,MASK mask )
}
else if (selectItemByPrefix(wstring_to_utf8str(mSearchString), FALSE))
{
- mNeedsScroll = TRUE;
+ mNeedsScroll = true;
// update search string only on successful match
mSearchTimer.reset();
@@ -2158,7 +2241,7 @@ BOOL LLScrollListCtrl::handleUnicodeCharHere(llwchar uni_char)
if (selectItemByPrefix(wstring_to_utf8str(mSearchString + (llwchar)uni_char), FALSE))
{
// update search string only on successful match
- mNeedsScroll = TRUE;
+ mNeedsScroll = true;
mSearchString += uni_char;
mSearchTimer.reset();
@@ -2204,7 +2287,7 @@ BOOL LLScrollListCtrl::handleUnicodeCharHere(llwchar uni_char)
if (item->getEnabled() && LLStringOps::toLower(item_label[0]) == uni_char)
{
selectItem(item);
- mNeedsScroll = TRUE;
+ mNeedsScroll = true;
cellp->highlightText(0, 1);
mSearchTimer.reset();
@@ -2275,7 +2358,7 @@ void LLScrollListCtrl::selectItem(LLScrollListItem* itemp, BOOL select_single_it
}
itemp->setSelected(TRUE);
mLastSelected = itemp;
- mSelectionChanged = TRUE;
+ mSelectionChanged = true;
}
}
@@ -2296,7 +2379,7 @@ void LLScrollListCtrl::deselectItem(LLScrollListItem* itemp)
{
cellp->highlightText(0, 0);
}
- mSelectionChanged = TRUE;
+ mSelectionChanged = true;
}
}
@@ -2304,7 +2387,7 @@ void LLScrollListCtrl::commitIfChanged()
{
if (mSelectionChanged)
{
- mDirty = TRUE;
+ mDirty = true;
mSelectionChanged = FALSE;
onCommit();
}
@@ -2415,7 +2498,8 @@ void LLScrollListCtrl::sortOnce(S32 column, BOOL ascending)
void LLScrollListCtrl::dirtyColumns()
{
- mColumnsDirty = TRUE;
+ mColumnsDirty = true;
+ mColumnWidthsDirty = true;
// need to keep mColumnsIndexed up to date
// just in case someone indexes into it immediately
@@ -2501,7 +2585,7 @@ void LLScrollListCtrl::copy()
{
buffer += (*itor)->getContentsCSV() + "\n";
}
- gClipboard.copyFromSubstring(utf8str_to_wstring(buffer), 0, buffer.length());
+ LLClipboard::instance().copyToClipboard(utf8str_to_wstring(buffer), 0, buffer.length());
}
// virtual
@@ -2596,7 +2680,7 @@ void LLScrollListCtrl::addColumn(const LLScrollListColumn::Params& column_params
}
if (new_column->mRelWidth >= 0)
{
- new_column->setWidth((S32)llround(new_column->mRelWidth*mItemListRect.getWidth()));
+ new_column->setWidth((S32)ll_round(new_column->mRelWidth*mItemListRect.getWidth()));
}
else if(new_column->mDynamicWidth)
{
@@ -2701,6 +2785,11 @@ BOOL LLScrollListCtrl::hasSortOrder() const
return !mSortColumns.empty();
}
+void LLScrollListCtrl::clearSortOrder()
+{
+ mSortColumns.clear();
+}
+
void LLScrollListCtrl::clearColumns()
{
column_map_t::iterator itor;
@@ -2752,10 +2841,10 @@ LLScrollListColumn* LLScrollListCtrl::getColumn(const std::string& name)
return NULL;
}
-LLFastTimer::DeclareTimer FTM_ADD_SCROLLLIST_ELEMENT("Add Scroll List Item");
+LLTrace::BlockTimerStatHandle FTM_ADD_SCROLLLIST_ELEMENT("Add Scroll List Item");
LLScrollListItem* LLScrollListCtrl::addElement(const LLSD& element, EAddPosition pos, void* userdata)
{
- LLFastTimer _(FTM_ADD_SCROLLLIST_ELEMENT);
+ LL_RECORD_BLOCK_TIME(FTM_ADD_SCROLLLIST_ELEMENT);
LLScrollListItem::Params item_params;
LLParamSDParser parser;
parser.readSD(element, item_params);
@@ -2765,14 +2854,14 @@ LLScrollListItem* LLScrollListCtrl::addElement(const LLSD& element, EAddPosition
LLScrollListItem* LLScrollListCtrl::addRow(const LLScrollListItem::Params& item_p, EAddPosition pos)
{
- LLFastTimer _(FTM_ADD_SCROLLLIST_ELEMENT);
+ LL_RECORD_BLOCK_TIME(FTM_ADD_SCROLLLIST_ELEMENT);
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)
{
- LLFastTimer _(FTM_ADD_SCROLLLIST_ELEMENT);
+ LL_RECORD_BLOCK_TIME(FTM_ADD_SCROLLLIST_ELEMENT);
if (!item_p.validateBlock() || !new_item) return NULL;
new_item->setNumColumns(mColumns.size());
@@ -2813,7 +2902,10 @@ LLScrollListItem* LLScrollListCtrl::addRow(LLScrollListItem *new_item, const LLS
}
S32 index = columnp->mIndex;
- cell_p.width.setIfNotProvided(columnp->getWidth());
+ if (!cell_p.width.isProvided())
+ {
+ cell_p.width = columnp->getWidth();
+ }
LLScrollListCell* cell = LLScrollListCell::create(cell_p);
@@ -2976,7 +3068,7 @@ void LLScrollListCtrl::resetDirty()
void LLScrollListCtrl::onFocusReceived()
{
// forget latent selection changes when getting focus
- mSelectionChanged = FALSE;
+ mSelectionChanged = false;
LLUICtrl::onFocusReceived();
}
diff --git a/indra/llui/llscrolllistctrl.h b/indra/llui/llscrolllistctrl.h
index 09ab89960d..6325a79cd5 100644..100755
--- a/indra/llui/llscrolllistctrl.h
+++ b/indra/llui/llscrolllistctrl.h
@@ -34,7 +34,6 @@
#include "lluictrl.h"
#include "llctrlselectioninterface.h"
-//#include "lldarray.h"
#include "llfontgl.h"
#include "llui.h"
#include "llstring.h" // LLWString
@@ -241,7 +240,7 @@ public:
// one of which can be selected at a time.
virtual LLScrollListItem* addSimpleElement(const std::string& value, EAddPosition pos = ADD_BOTTOM, const LLSD& id = LLSD());
- BOOL selectItemByLabel( const std::string& item, BOOL case_sensitive = TRUE ); // FALSE if item not found
+ BOOL selectItemByLabel( const std::string& item, BOOL case_sensitive = TRUE, S32 column = 0 ); // FALSE if item not found
BOOL selectItemByPrefix(const std::string& target, BOOL case_sensitive = TRUE);
BOOL selectItemByPrefix(const LLWString& target, BOOL case_sensitive = TRUE);
LLScrollListItem* getItemByLabel( const std::string& item, BOOL case_sensitive = TRUE, S32 column = 0 );
@@ -257,6 +256,7 @@ public:
LLScrollListItem* getFirstSelected() const;
virtual S32 getFirstSelectedIndex() const;
std::vector<LLScrollListItem*> getAllSelected() const;
+ S32 getNumSelected() const;
LLScrollListItem* getLastSelectedItem() const { return mLastSelected; }
// iterate over all items
@@ -341,9 +341,9 @@ public:
static void onClickColumn(void *userdata);
- virtual void updateColumns();
- void calcColumnWidths();
- S32 getMaxContentWidth() { return mMaxContentWidth; }
+ virtual void updateColumns(bool force_update = false);
+ S32 calcMaxContentWidth();
+ bool updateColumnWidths();
void setHeadingHeight(S32 heading_height);
/**
@@ -373,6 +373,7 @@ public:
std::string getSortColumnName();
BOOL getSortAscending() { return mSortColumns.empty() ? TRUE : mSortColumns.back().second; }
BOOL hasSortOrder() const;
+ void clearSortOrder();
S32 selectMultiple( uuid_vec_t ids );
// conceptually const, but mutates mItemList
@@ -428,6 +429,9 @@ private:
BOOL setSort(S32 column, BOOL ascending);
S32 getLinesPerPage();
+ static void showProfile(std::string id, bool is_group);
+ static void sendIM(std::string id);
+ static void addFriend(std::string id);
static void 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);
@@ -438,16 +442,17 @@ private:
S32 mHeadingHeight; // the height of the column header buttons, if visible
U32 mMaxSelectable;
LLScrollbar* mScrollbar;
- BOOL mAllowMultipleSelection;
- BOOL mAllowKeyboardMovement;
- BOOL mCommitOnKeyboardMovement;
- BOOL mCommitOnSelectionChange;
- BOOL mSelectionChanged;
- BOOL mNeedsScroll;
- BOOL mMouseWheelOpaque;
- BOOL mCanSelect;
- const BOOL mDisplayColumnHeaders;
- BOOL mColumnsDirty;
+ bool mAllowMultipleSelection;
+ bool mAllowKeyboardMovement;
+ bool mCommitOnKeyboardMovement;
+ bool mCommitOnSelectionChange;
+ bool mSelectionChanged;
+ bool mNeedsScroll;
+ bool mMouseWheelOpaque;
+ bool mCanSelect;
+ bool mDisplayColumnHeaders;
+ bool mColumnsDirty;
+ bool mColumnWidthsDirty;
mutable item_list mItemList;
@@ -456,7 +461,6 @@ private:
S32 mMaxItemCount;
LLRect mItemListRect;
- S32 mMaxContentWidth;
S32 mColumnPadding;
BOOL mBackgroundVisible;
@@ -480,6 +484,8 @@ private:
S32 mHighlightedItem;
class LLViewBorder* mBorder;
LLContextMenu *mPopupMenu;
+
+ LLView *mCommentTextView;
LLWString mSearchString;
LLFrameTimer mSearchTimer;
@@ -494,7 +500,7 @@ private:
typedef std::map<std::string, LLScrollListColumn*> column_map_t;
column_map_t mColumns;
- BOOL mDirty;
+ bool mDirty;
S32 mOriginalSelection;
ContextMenuType mContextMenuType;
diff --git a/indra/llui/llscrolllistitem.cpp b/indra/llui/llscrolllistitem.cpp
index d95752e31c..df22c88afb 100644..100755
--- a/indra/llui/llscrolllistitem.cpp
+++ b/indra/llui/llscrolllistitem.cpp
@@ -50,6 +50,7 @@ LLScrollListItem::LLScrollListItem( const Params& p )
LLScrollListItem::~LLScrollListItem()
{
std::for_each(mColumns.begin(), mColumns.end(), DeletePointer());
+ mColumns.clear();
}
void LLScrollListItem::addColumn(const LLScrollListCell::Params& p)
@@ -82,7 +83,7 @@ void LLScrollListItem::setColumn( S32 column, LLScrollListCell *cell )
}
else
{
- llerrs << "LLScrollListItem::setColumn: bad column: " << column << llendl;
+ LL_ERRS() << "LLScrollListItem::setColumn: bad column: " << column << LL_ENDL;
}
}
@@ -138,7 +139,7 @@ void LLScrollListItem::draw(const LLRect& rect, const LLColor4& fg_color, const
LLUI::pushMatrix();
{
- LLUI::translate((F32) cur_x, (F32) rect.mBottom, 0.0f);
+ LLUI::translate((F32) cur_x, (F32) rect.mBottom);
cell->draw( fg_color, highlight_color );
}
diff --git a/indra/llui/llscrolllistitem.h b/indra/llui/llscrolllistitem.h
index 611df729b4..13655b5873 100644..100755
--- a/indra/llui/llscrolllistitem.h
+++ b/indra/llui/llscrolllistitem.h
@@ -33,10 +33,10 @@
#include "v4color.h"
#include "llinitparam.h"
#include "llscrolllistcell.h"
+#include "llcoord.h"
#include <vector>
-class LLCoordGL;
class LLCheckBoxCtrl;
class LLResizeBar;
class LLScrollListCtrl;
diff --git a/indra/llui/llsdparam.cpp b/indra/llui/llsdparam.cpp
deleted file mode 100644
index 9ad13054cb..0000000000
--- a/indra/llui/llsdparam.cpp
+++ /dev/null
@@ -1,314 +0,0 @@
-/**
- * @file llsdparam.cpp
- * @brief parameter block abstraction for creating complex objects and
- * parsing construction parameters from xml and LLSD
- *
- * $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"
-
-// Project includes
-#include "llsdparam.h"
-#include "llsdutil.h"
-
-static LLInitParam::Parser::parser_read_func_map_t sReadFuncs;
-static LLInitParam::Parser::parser_write_func_map_t sWriteFuncs;
-static LLInitParam::Parser::parser_inspect_func_map_t sInspectFuncs;
-
-//
-// LLParamSDParser
-//
-LLParamSDParser::LLParamSDParser()
-: Parser(sReadFuncs, sWriteFuncs, sInspectFuncs)
-{
- using boost::bind;
-
- if (sReadFuncs.empty())
- {
- registerParserFuncs<LLInitParam::NoParamValue>(readNoValue, &LLParamSDParser::writeNoValue);
- registerParserFuncs<S32>(readS32, &LLParamSDParser::writeTypedValue<S32>);
- registerParserFuncs<U32>(readU32, &LLParamSDParser::writeU32Param);
- registerParserFuncs<F32>(readF32, &LLParamSDParser::writeTypedValue<F32>);
- registerParserFuncs<F64>(readF64, &LLParamSDParser::writeTypedValue<F64>);
- registerParserFuncs<bool>(readBool, &LLParamSDParser::writeTypedValue<bool>);
- registerParserFuncs<std::string>(readString, &LLParamSDParser::writeTypedValue<std::string>);
- registerParserFuncs<LLUUID>(readUUID, &LLParamSDParser::writeTypedValue<LLUUID>);
- registerParserFuncs<LLDate>(readDate, &LLParamSDParser::writeTypedValue<LLDate>);
- registerParserFuncs<LLURI>(readURI, &LLParamSDParser::writeTypedValue<LLURI>);
- registerParserFuncs<LLSD>(readSD, &LLParamSDParser::writeTypedValue<LLSD>);
- }
-}
-
-// special case handling of U32 due to ambiguous LLSD::assign overload
-bool LLParamSDParser::writeU32Param(LLParamSDParser::parser_t& parser, const void* val_ptr, const parser_t::name_stack_t& name_stack)
-{
- LLParamSDParser& sdparser = static_cast<LLParamSDParser&>(parser);
- if (!sdparser.mWriteRootSD) return false;
-
- LLSD* sd_to_write = sdparser.getSDWriteNode(name_stack);
- if (!sd_to_write) return false;
-
- sd_to_write->assign((S32)*((const U32*)val_ptr));
- return true;
-}
-
-bool LLParamSDParser::writeNoValue(LLParamSDParser::parser_t& parser, const void* val_ptr, const parser_t::name_stack_t& name_stack)
-{
- LLParamSDParser& sdparser = static_cast<LLParamSDParser&>(parser);
- if (!sdparser.mWriteRootSD) return false;
-
- LLSD* sd_to_write = sdparser.getSDWriteNode(name_stack);
- if (!sd_to_write) return false;
-
- return true;
-}
-
-
-void LLParamSDParser::readSD(const LLSD& sd, LLInitParam::BaseBlock& block, bool silent)
-{
- mCurReadSD = NULL;
- mNameStack.clear();
- setParseSilently(silent);
-
- readSDValues(sd, block);
-}
-
-void LLParamSDParser::writeSD(LLSD& sd, const LLInitParam::BaseBlock& block)
-{
- mNameStack.clear();
- mWriteRootSD = &sd;
- block.serializeBlock(*this);
-}
-
-const LLSD NO_VALUE_MARKER;
-
-void LLParamSDParser::readSDValues(const LLSD& sd, LLInitParam::BaseBlock& block)
-{
- if (sd.isMap())
- {
- for (LLSD::map_const_iterator it = sd.beginMap();
- it != sd.endMap();
- ++it)
- {
- mNameStack.push_back(make_pair(it->first, newParseGeneration()));
- readSDValues(it->second, block);
- mNameStack.pop_back();
- }
- }
- else if (sd.isArray())
- {
- for (LLSD::array_const_iterator it = sd.beginArray();
- it != sd.endArray();
- ++it)
- {
- mNameStack.back().second = newParseGeneration();
- readSDValues(*it, block);
- }
- }
- else if (sd.isUndefined())
- {
- mCurReadSD = &NO_VALUE_MARKER;
- block.submitValue(mNameStack, *this);
- }
- else
- {
- mCurReadSD = &sd;
- block.submitValue(mNameStack, *this);
- }
-}
-
-/*virtual*/ std::string LLParamSDParser::getCurrentElementName()
-{
- std::string full_name = "sd";
- for (name_stack_t::iterator it = mNameStack.begin();
- it != mNameStack.end();
- ++it)
- {
- full_name += llformat("[%s]", it->first.c_str());
- }
-
- return full_name;
-}
-
-LLSD* LLParamSDParser::getSDWriteNode(const parser_t::name_stack_t& name_stack)
-{
- //TODO: implement nested LLSD writing
- LLSD* sd_to_write = mWriteRootSD;
- bool new_traversal = false;
- for (name_stack_t::const_iterator it = name_stack.begin(), prev_it = mNameStack.begin();
- it != name_stack.end();
- ++it)
- {
- bool new_array_entry = false;
- if (prev_it == mNameStack.end())
- {
- new_traversal = true;
- }
- else
- {
- if (!new_traversal // have not diverged yet from previous trace
- && prev_it->first == it->first // names match
- && prev_it->second != it->second) // versions differ
- {
- // name stacks match, but version numbers differ in last place.
- // create a different entry at this point using an LLSD array
- new_array_entry = true;
- }
- if (prev_it->first != it->first // names differ
- || prev_it->second != it->second) // versions differ
- {
- // at this point we have diverged from our last trace
- // so any elements referenced here are new
- new_traversal = true;
- }
- }
-
- LLSD* child_sd = &(*sd_to_write)[it->first];
-
- if (child_sd->isArray())
- {
- if (new_traversal)
- {
- // write to new element at end
- sd_to_write = &(*child_sd)[child_sd->size()];
- }
- else
- {
- // write to last of existing elements, or first element if empty
- sd_to_write = &(*child_sd)[llmax(0, child_sd->size() - 1)];
- }
- }
- else
- {
- if (new_array_entry && !child_sd->isArray())
- {
- // copy child contents into first element of an array
- LLSD new_array = LLSD::emptyArray();
- new_array.append(*child_sd);
- // assign array to slot that previously held the single value
- *child_sd = new_array;
- // return next element in that array
- sd_to_write = &((*child_sd)[1]);
- }
- else
- {
- sd_to_write = child_sd;
- }
- }
- if (prev_it != mNameStack.end())
- {
- ++prev_it;
- }
- }
- mNameStack = name_stack;
-
- //llinfos << ll_pretty_print_sd(*mWriteRootSD) << llendl;
- return sd_to_write;
-}
-
-bool LLParamSDParser::readNoValue(Parser& parser, void* val_ptr)
-{
- LLParamSDParser& self = static_cast<LLParamSDParser&>(parser);
- return self.mCurReadSD == &NO_VALUE_MARKER;
-}
-
-
-bool LLParamSDParser::readS32(Parser& parser, void* val_ptr)
-{
- LLParamSDParser& self = static_cast<LLParamSDParser&>(parser);
-
- *((S32*)val_ptr) = self.mCurReadSD->asInteger();
- return true;
-}
-
-bool LLParamSDParser::readU32(Parser& parser, void* val_ptr)
-{
- LLParamSDParser& self = static_cast<LLParamSDParser&>(parser);
-
- *((U32*)val_ptr) = self.mCurReadSD->asInteger();
- return true;
-}
-
-bool LLParamSDParser::readF32(Parser& parser, void* val_ptr)
-{
- LLParamSDParser& self = static_cast<LLParamSDParser&>(parser);
-
- *((F32*)val_ptr) = self.mCurReadSD->asReal();
- return true;
-}
-
-bool LLParamSDParser::readF64(Parser& parser, void* val_ptr)
-{
- LLParamSDParser& self = static_cast<LLParamSDParser&>(parser);
-
- *((F64*)val_ptr) = self.mCurReadSD->asReal();
- return true;
-}
-
-bool LLParamSDParser::readBool(Parser& parser, void* val_ptr)
-{
- LLParamSDParser& self = static_cast<LLParamSDParser&>(parser);
-
- *((bool*)val_ptr) = self.mCurReadSD->asBoolean();
- return true;
-}
-
-bool LLParamSDParser::readString(Parser& parser, void* val_ptr)
-{
- LLParamSDParser& self = static_cast<LLParamSDParser&>(parser);
-
- *((std::string*)val_ptr) = self.mCurReadSD->asString();
- return true;
-}
-
-bool LLParamSDParser::readUUID(Parser& parser, void* val_ptr)
-{
- LLParamSDParser& self = static_cast<LLParamSDParser&>(parser);
-
- *((LLUUID*)val_ptr) = self.mCurReadSD->asUUID();
- return true;
-}
-
-bool LLParamSDParser::readDate(Parser& parser, void* val_ptr)
-{
- LLParamSDParser& self = static_cast<LLParamSDParser&>(parser);
-
- *((LLDate*)val_ptr) = self.mCurReadSD->asDate();
- return true;
-}
-
-bool LLParamSDParser::readURI(Parser& parser, void* val_ptr)
-{
- LLParamSDParser& self = static_cast<LLParamSDParser&>(parser);
-
- *((LLURI*)val_ptr) = self.mCurReadSD->asURI();
- return true;
-}
-
-bool LLParamSDParser::readSD(Parser& parser, void* val_ptr)
-{
- LLParamSDParser& self = static_cast<LLParamSDParser&>(parser);
-
- *((LLSD*)val_ptr) = *self.mCurReadSD;
- return true;
-}
diff --git a/indra/llui/llsdparam.h b/indra/llui/llsdparam.h
deleted file mode 100644
index 69dab2b411..0000000000
--- a/indra/llui/llsdparam.h
+++ /dev/null
@@ -1,104 +0,0 @@
-/**
- * @file llsdparam.h
- * @brief parameter block abstraction for creating complex objects and
- * parsing construction parameters from xml and LLSD
- *
- * $LicenseInfo:firstyear=2008&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLSDPARAM_H
-#define LL_LLSDPARAM_H
-
-#include "llinitparam.h"
-
-class LLParamSDParser
-: public LLInitParam::Parser
-{
-LOG_CLASS(LLParamSDParser);
-
-typedef LLInitParam::Parser parser_t;
-
-public:
- LLParamSDParser();
- void readSD(const LLSD& sd, LLInitParam::BaseBlock& block, bool silent = false);
- void writeSD(LLSD& sd, const LLInitParam::BaseBlock& block);
-
- /*virtual*/ std::string getCurrentElementName();
-
-private:
- void readSDValues(const LLSD& sd, LLInitParam::BaseBlock& block);
-
- template<typename T>
- static bool writeTypedValue(Parser& parser, const void* val_ptr, const parser_t::name_stack_t& name_stack)
- {
- LLParamSDParser& sdparser = static_cast<LLParamSDParser&>(parser);
- if (!sdparser.mWriteRootSD) return false;
-
- LLSD* sd_to_write = sdparser.getSDWriteNode(name_stack);
- if (!sd_to_write) return false;
-
- sd_to_write->assign(*((const T*)val_ptr));
- return true;
- }
-
- LLSD* getSDWriteNode(const parser_t::name_stack_t& name_stack);
-
- static bool writeU32Param(Parser& parser, const void* value_ptr, const parser_t::name_stack_t& name_stack);
- static bool writeNoValue(Parser& parser, const void* value_ptr, const parser_t::name_stack_t& name_stack);
-
- static bool readNoValue(Parser& parser, void* val_ptr);
- static bool readS32(Parser& parser, void* val_ptr);
- static bool readU32(Parser& parser, void* val_ptr);
- static bool readF32(Parser& parser, void* val_ptr);
- static bool readF64(Parser& parser, void* val_ptr);
- static bool readBool(Parser& parser, void* val_ptr);
- static bool readString(Parser& parser, void* val_ptr);
- static bool readUUID(Parser& parser, void* val_ptr);
- static bool readDate(Parser& parser, void* val_ptr);
- static bool readURI(Parser& parser, void* val_ptr);
- static bool readSD(Parser& parser, void* val_ptr);
-
- Parser::name_stack_t mNameStack;
- const LLSD* mCurReadSD;
- LLSD* mWriteRootSD;
- LLSD* mCurWriteSD;
-};
-
-template<typename T>
-class LLSDParamAdapter : public T
- {
- public:
- LLSDParamAdapter() {}
- LLSDParamAdapter(const LLSD& sd)
- {
- LLParamSDParser parser;
- parser.readSD(sd, *this);
- }
-
- LLSDParamAdapter(const T& val)
- {
- T::operator=(val);
- }
- };
-
-#endif // LL_LLSDPARAM_H
-
diff --git a/indra/llui/llsearcheditor.cpp b/indra/llui/llsearcheditor.cpp
index ea96fc573d..ea96fc573d 100644..100755
--- a/indra/llui/llsearcheditor.cpp
+++ b/indra/llui/llsearcheditor.cpp
diff --git a/indra/llui/llsearcheditor.h b/indra/llui/llsearcheditor.h
index f5c3b532c4..c2d7916938 100644..100755
--- a/indra/llui/llsearcheditor.h
+++ b/indra/llui/llsearcheditor.h
@@ -55,9 +55,7 @@ public:
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); }
diff --git a/indra/llui/llslider.cpp b/indra/llui/llslider.cpp
index 013950a5ad..ebbb951ee6 100644..100755
--- a/indra/llui/llslider.cpp
+++ b/indra/llui/llslider.cpp
@@ -54,9 +54,7 @@ LLSlider::Params::Params()
track_highlight_vertical_image("track_highlight_vertical_image"),
mouse_down_callback("mouse_down_callback"),
mouse_up_callback("mouse_up_callback")
-{
- follows.flags(FOLLOWS_LEFT | FOLLOWS_TOP);
-}
+{}
LLSlider::LLSlider(const LLSlider::Params& p)
: LLF32UICtrl(p),
@@ -190,12 +188,12 @@ BOOL LLSlider::handleHover(S32 x, S32 y, MASK mask)
setValueAndCommit(t * (mMaxValue - mMinValue) + mMinValue );
}
getWindow()->setCursor(UI_CURSOR_ARROW);
- lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (active)" << llendl;
+ LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (active)" << LL_ENDL;
}
else
{
getWindow()->setCursor(UI_CURSOR_ARROW);
- lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (inactive)" << llendl;
+ LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (inactive)" << LL_ENDL;
}
return TRUE;
}
diff --git a/indra/llui/llslider.h b/indra/llui/llslider.h
index 68823ed68e..3b492d8182 100644..100755
--- a/indra/llui/llslider.h
+++ b/indra/llui/llslider.h
@@ -29,12 +29,11 @@
#include "llf32uictrl.h"
#include "v4color.h"
+#include "lluiimage.h"
class LLSlider : public LLF32UICtrl
{
public:
- enum ORIENTATION { HORIZONTAL, VERTICAL };
-
struct Params : public LLInitParam::Block<Params, LLF32UICtrl::Params>
{
Optional<std::string> orientation;
@@ -97,7 +96,7 @@ private:
LLPointer<LLUIImage> mTrackHighlightHorizontalImage;
LLPointer<LLUIImage> mTrackHighlightVerticalImage;
- const ORIENTATION mOrientation;
+ const EOrientation mOrientation;
LLRect mThumbRect;
LLUIColor mTrackColor;
diff --git a/indra/llui/llsliderctrl.cpp b/indra/llui/llsliderctrl.cpp
index d760178e35..0056cb6dc4 100644..100755
--- a/indra/llui/llsliderctrl.cpp
+++ b/indra/llui/llsliderctrl.cpp
@@ -43,8 +43,6 @@
#include "llresmgr.h"
#include "lluictrlfactory.h"
-const U32 MAX_STRING_LENGTH = 10;
-
static LLDefaultChildRegistry::Register<LLSliderCtrl> r("slider");
LLSliderCtrl::LLSliderCtrl(const LLSliderCtrl::Params& p)
@@ -58,7 +56,8 @@ LLSliderCtrl::LLSliderCtrl(const LLSliderCtrl::Params& p)
mPrecision(p.decimal_digits),
mTextEnabledColor(p.text_color()),
mTextDisabledColor(p.text_disabled_color()),
- mLabelWidth(p.label_width)
+ mLabelWidth(p.label_width),
+ mEditorCommitSignal(NULL)
{
S32 top = getRect().getHeight();
S32 bottom = 0;
@@ -76,8 +75,14 @@ LLSliderCtrl::LLSliderCtrl(const LLSliderCtrl::Params& p)
}
LLRect label_rect( left, top, label_width, bottom );
LLTextBox::Params params(p.slider_label);
- params.rect.setIfNotProvided(label_rect);
- params.font.setIfNotProvided(p.font);
+ if (!params.rect.isProvided())
+ {
+ params.rect = label_rect;
+ }
+ if (!params.font.isProvided())
+ {
+ params.font = p.font;
+ }
params.initial_value(p.label());
mLabelBox = LLUICtrlFactory::create<LLTextBox> (params);
addChild(mLabelBox);
@@ -113,15 +118,33 @@ LLSliderCtrl::LLSliderCtrl(const LLSliderCtrl::Params& p)
S32 slider_left = label_width ? label_width + sliderctrl_spacing : 0;
LLSlider::Params slider_p(p.slider_bar);
slider_p.name("slider_bar");
- slider_p.rect.setIfNotProvided(LLRect(slider_left,top,slider_right,bottom));
- slider_p.initial_value.setIfNotProvided(p.initial_value().asReal());
- 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);
+ if (!slider_p.rect.isProvided())
+ {
+ slider_p.rect = LLRect(slider_left,top,slider_right,bottom);
+ }
+ if (!slider_p.initial_value.isProvided())
+ {
+ slider_p.initial_value = p.initial_value().asReal();
+ }
+ if (!slider_p.min_value.isProvided())
+ {
+ slider_p.min_value = p.min_value;
+ }
+ if (!slider_p.max_value.isProvided())
+ {
+ slider_p.max_value = p.max_value;
+ }
+ if (!slider_p.increment.isProvided())
+ {
+ slider_p.increment = p.increment;
+ }
+ if (!slider_p.orientation.isProvided())
+ {
+ slider_p.orientation = p.orientation;
+ }
- slider_p.commit_callback.function(&LLSliderCtrl::onSliderCommit);
- slider_p.control_name(p.control_name);
+ slider_p.commit_callback.function = &LLSliderCtrl::onSliderCommit;
+ slider_p.control_name = p.control_name;
slider_p.mouse_down_callback( p.mouse_down_callback );
slider_p.mouse_up_callback( p.mouse_up_callback );
mSlider = LLUICtrlFactory::create<LLSlider> (slider_p);
@@ -134,8 +157,15 @@ LLSliderCtrl::LLSliderCtrl(const LLSliderCtrl::Params& p)
if( p.can_edit_text() )
{
LLLineEditor::Params line_p(p.value_editor);
- line_p.rect.setIfNotProvided(text_rect);
- line_p.font.setIfNotProvided(p.font);
+ if (!line_p.rect.isProvided())
+ {
+ line_p.rect = text_rect;
+ }
+ if (!line_p.font.isProvided())
+ {
+ line_p.font = p.font;
+ }
+
line_p.commit_callback.function(&LLSliderCtrl::onEditorCommit);
line_p.prevalidate_callback(&LLTextValidate::validateFloat);
mEditor = LLUICtrlFactory::create<LLLineEditor>(line_p);
@@ -149,8 +179,14 @@ LLSliderCtrl::LLSliderCtrl(const LLSliderCtrl::Params& p)
else
{
LLTextBox::Params text_p(p.value_text);
- text_p.rect.setIfNotProvided(text_rect);
- text_p.font.setIfNotProvided(p.font);
+ if (!text_p.rect.isProvided())
+ {
+ text_p.rect = text_rect;
+ }
+ if (!text_p.font.isProvided())
+ {
+ text_p.font = p.font;
+ }
mTextBox = LLUICtrlFactory::create<LLTextBox>(text_p);
addChild(mTextBox);
}
@@ -159,6 +195,11 @@ LLSliderCtrl::LLSliderCtrl(const LLSliderCtrl::Params& p)
updateText();
}
+LLSliderCtrl::~LLSliderCtrl()
+{
+ delete mEditorCommitSignal;
+}
+
// static
void LLSliderCtrl::onEditorGainFocus( LLFocusableElement* caller, void *userdata )
{
@@ -271,6 +312,8 @@ void LLSliderCtrl::onEditorCommit( LLUICtrl* ctrl, const LLSD& userdata )
if( success )
{
self->onCommit();
+ if (self->mEditorCommitSignal)
+ (*(self->mEditorCommitSignal))(self, self->getValueF32());
}
else
{
@@ -366,7 +409,7 @@ void LLSliderCtrl::setPrecision(S32 precision)
{
if (precision < 0 || precision > 10)
{
- llerrs << "LLSliderCtrl::setPrecision - precision out of range" << llendl;
+ LL_ERRS() << "LLSliderCtrl::setPrecision - precision out of range" << LL_ENDL;
return;
}
@@ -384,6 +427,11 @@ boost::signals2::connection LLSliderCtrl::setSliderMouseUpCallback( const commit
return mSlider->setMouseUpCallback( cb );
}
+boost::signals2::connection LLSliderCtrl::setSliderEditorCommitCallback( const commit_signal_t::slot_type& cb )
+{
+ if (!mEditorCommitSignal) mEditorCommitSignal = new commit_signal_t();
+ return mEditorCommitSignal->connect(cb);
+}
void LLSliderCtrl::onTabInto()
{
if( mEditor )
diff --git a/indra/llui/llsliderctrl.h b/indra/llui/llsliderctrl.h
index 5153e33f49..67cca9ef04 100644..100755
--- a/indra/llui/llsliderctrl.h
+++ b/indra/llui/llsliderctrl.h
@@ -81,7 +81,7 @@ protected:
LLSliderCtrl(const Params&);
friend class LLUICtrlFactory;
public:
- virtual ~LLSliderCtrl() {} // Children all cleaned up by default view destructor.
+ virtual ~LLSliderCtrl();
/*virtual*/ F32 getValueF32() const { return mSlider->getValueF32(); }
void setValue(F32 v, BOOL from_event = FALSE);
@@ -112,6 +112,7 @@ public:
boost::signals2::connection setSliderMouseDownCallback( const commit_signal_t::slot_type& cb );
boost::signals2::connection setSliderMouseUpCallback( const commit_signal_t::slot_type& cb );
+ boost::signals2::connection setSliderEditorCommitCallback( const commit_signal_t::slot_type& cb );
/*virtual*/ void onTabInto();
@@ -150,6 +151,8 @@ private:
LLUIColor mTextEnabledColor;
LLUIColor mTextDisabledColor;
+
+ commit_signal_t* mEditorCommitSignal;
};
#endif // LL_LLSLIDERCTRL_H
diff --git a/indra/llui/llspellcheck.cpp b/indra/llui/llspellcheck.cpp
new file mode 100755
index 0000000000..0db4281059
--- /dev/null
+++ b/indra/llui/llspellcheck.cpp
@@ -0,0 +1,512 @@
+/**
+ * @file llspellcheck.cpp
+ * @brief Spell checking functionality
+ *
+ * $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 "lldir.h"
+#include "llsdserialize.h"
+
+#include "llspellcheck.h"
+#if LL_WINDOWS
+ #include <hunspell/hunspelldll.h>
+ #pragma comment(lib, "libhunspell.lib")
+#else
+ #include <hunspell/hunspell.hxx>
+#endif
+
+static const std::string DICT_DIR = "dictionaries";
+static const std::string DICT_FILE_CUSTOM = "user_custom.dic";
+static const std::string DICT_FILE_IGNORE = "user_ignore.dic";
+
+static const std::string DICT_FILE_MAIN = "dictionaries.xml";
+static const std::string DICT_FILE_USER = "user_dictionaries.xml";
+
+LLSD LLSpellChecker::sDictMap;
+LLSpellChecker::settings_change_signal_t LLSpellChecker::sSettingsChangeSignal;
+
+LLSpellChecker::LLSpellChecker()
+ : mHunspell(NULL)
+{
+ // Load initial dictionary information
+ refreshDictionaryMap();
+}
+
+LLSpellChecker::~LLSpellChecker()
+{
+ delete mHunspell;
+}
+
+bool LLSpellChecker::checkSpelling(const std::string& word) const
+{
+ if ( (!mHunspell) || (word.length() < 3) || (0 != mHunspell->spell(word.c_str())) )
+ {
+ return true;
+ }
+ if (mIgnoreList.size() > 0)
+ {
+ std::string word_lower(word);
+ LLStringUtil::toLower(word_lower);
+ return (mIgnoreList.end() != std::find(mIgnoreList.begin(), mIgnoreList.end(), word_lower));
+ }
+ return false;
+}
+
+S32 LLSpellChecker::getSuggestions(const std::string& word, std::vector<std::string>& suggestions) const
+{
+ suggestions.clear();
+ if ( (!mHunspell) || (word.length() < 3) )
+ {
+ return 0;
+ }
+
+ char** suggestion_list; int suggestion_cnt = 0;
+ if ( (suggestion_cnt = mHunspell->suggest(&suggestion_list, word.c_str())) != 0 )
+ {
+ for (int suggestion_index = 0; suggestion_index < suggestion_cnt; suggestion_index++)
+ {
+ suggestions.push_back(suggestion_list[suggestion_index]);
+ }
+ mHunspell->free_list(&suggestion_list, suggestion_cnt);
+ }
+ return suggestions.size();
+}
+
+// static
+const LLSD LLSpellChecker::getDictionaryData(const std::string& dict_language)
+{
+ for (LLSD::array_const_iterator it = sDictMap.beginArray(); it != sDictMap.endArray(); ++it)
+ {
+ const LLSD& dict_entry = *it;
+ if (dict_language == dict_entry["language"].asString())
+ {
+ return dict_entry;
+ }
+ }
+ return LLSD();
+}
+
+// static
+bool LLSpellChecker::hasDictionary(const std::string& dict_language, bool check_installed)
+{
+ const LLSD dict_info = getDictionaryData(dict_language);
+ return dict_info.has("language") && ( (!check_installed) || (dict_info["installed"].asBoolean()) );
+}
+
+// static
+void LLSpellChecker::setDictionaryData(const LLSD& dict_info)
+{
+ const std::string dict_language = dict_info["language"].asString();
+ if (dict_language.empty())
+ {
+ return;
+ }
+
+ for (LLSD::array_iterator it = sDictMap.beginArray(); it != sDictMap.endArray(); ++it)
+ {
+ LLSD& dict_entry = *it;
+ if (dict_language == dict_entry["language"].asString())
+ {
+ dict_entry = dict_info;
+ return;
+ }
+ }
+ sDictMap.append(dict_info);
+ return;
+}
+
+// static
+void LLSpellChecker::refreshDictionaryMap()
+{
+ const std::string app_path = getDictionaryAppPath();
+ const std::string user_path = getDictionaryUserPath();
+
+ // Load dictionary information (file name, friendly name, ...)
+ std::string user_filename(user_path + DICT_FILE_MAIN);
+ llifstream user_file(user_filename.c_str(), std::ios::binary);
+ if ( (!user_file.is_open())
+ || (LLSDParser::PARSE_FAILURE == LLSDSerialize::fromXMLDocument(sDictMap, user_file))
+ || (0 == sDictMap.size()) )
+ {
+ std::string app_filename(app_path + DICT_FILE_MAIN);
+ llifstream app_file(app_filename.c_str(), std::ios::binary);
+ if ( (!app_file.is_open())
+ || (LLSDParser::PARSE_FAILURE == LLSDSerialize::fromXMLDocument(sDictMap, app_file))
+ || (0 == sDictMap.size()) )
+ {
+ return;
+ }
+ }
+
+ // Load user installed dictionary information
+ llifstream custom_file(user_filename.c_str(), std::ios::binary);
+ if (custom_file.is_open())
+ {
+ LLSD custom_dict_map;
+ LLSDSerialize::fromXMLDocument(custom_dict_map, custom_file);
+ for (LLSD::array_iterator it = custom_dict_map.beginArray(); it != custom_dict_map.endArray(); ++it)
+ {
+ LLSD& dict_info = *it;
+ dict_info["user_installed"] = true;
+ setDictionaryData(dict_info);
+ }
+ custom_file.close();
+ }
+
+ // Look for installed dictionaries
+ std::string tmp_app_path, tmp_user_path;
+ for (LLSD::array_iterator it = sDictMap.beginArray(); it != sDictMap.endArray(); ++it)
+ {
+ LLSD& sdDict = *it;
+ tmp_app_path = (sdDict.has("name")) ? app_path + sdDict["name"].asString() : LLStringUtil::null;
+ tmp_user_path = (sdDict.has("name")) ? user_path + sdDict["name"].asString() : LLStringUtil::null;
+ sdDict["installed"] =
+ (!tmp_app_path.empty()) && ((gDirUtilp->fileExists(tmp_user_path + ".dic")) || (gDirUtilp->fileExists(tmp_app_path + ".dic")));
+ }
+
+ sSettingsChangeSignal();
+}
+
+void LLSpellChecker::addToCustomDictionary(const std::string& word)
+{
+ if (mHunspell)
+ {
+ mHunspell->add(word.c_str());
+ }
+ addToDictFile(getDictionaryUserPath() + DICT_FILE_CUSTOM, word);
+ sSettingsChangeSignal();
+}
+
+void LLSpellChecker::addToIgnoreList(const std::string& word)
+{
+ std::string word_lower(word);
+ LLStringUtil::toLower(word_lower);
+ if (mIgnoreList.end() == std::find(mIgnoreList.begin(), mIgnoreList.end(), word_lower))
+ {
+ mIgnoreList.push_back(word_lower);
+ addToDictFile(getDictionaryUserPath() + DICT_FILE_IGNORE, word_lower);
+ sSettingsChangeSignal();
+ }
+}
+
+void LLSpellChecker::addToDictFile(const std::string& dict_path, const std::string& word)
+{
+ std::vector<std::string> word_list;
+
+ if (gDirUtilp->fileExists(dict_path))
+ {
+ llifstream file_in(dict_path.c_str(), std::ios::in);
+ if (file_in.is_open())
+ {
+ std::string word; int line_num = 0;
+ while (getline(file_in, word))
+ {
+ // Skip over the first line since that's just a line count
+ if (0 != line_num)
+ {
+ word_list.push_back(word);
+ }
+ line_num++;
+ }
+ }
+ else
+ {
+ // TODO: show error message?
+ return;
+ }
+ }
+
+ word_list.push_back(word);
+
+ llofstream file_out(dict_path.c_str(), std::ios::out | std::ios::trunc);
+ if (file_out.is_open())
+ {
+ file_out << word_list.size() << std::endl;
+ for (std::vector<std::string>::const_iterator itWord = word_list.begin(); itWord != word_list.end(); ++itWord)
+ {
+ file_out << *itWord << std::endl;
+ }
+ file_out.close();
+ }
+}
+
+bool LLSpellChecker::isActiveDictionary(const std::string& dict_language) const
+{
+ return
+ (mDictLanguage == dict_language) ||
+ (mDictSecondary.end() != std::find(mDictSecondary.begin(), mDictSecondary.end(), dict_language));
+}
+
+void LLSpellChecker::setSecondaryDictionaries(dict_list_t dict_list)
+{
+ if (!getUseSpellCheck())
+ {
+ return;
+ }
+
+ // Check if we're only adding secondary dictionaries, or removing them
+ dict_list_t dict_add(llmax(dict_list.size(), mDictSecondary.size())), dict_rem(llmax(dict_list.size(), mDictSecondary.size()));
+ dict_list.sort();
+ mDictSecondary.sort();
+ dict_list_t::iterator end_added = std::set_difference(dict_list.begin(), dict_list.end(), mDictSecondary.begin(), mDictSecondary.end(), dict_add.begin());
+ dict_list_t::iterator end_removed = std::set_difference(mDictSecondary.begin(), mDictSecondary.end(), dict_list.begin(), dict_list.end(), dict_rem.begin());
+
+ if (end_removed != dict_rem.begin()) // We can't remove secondary dictionaries so we need to recreate the Hunspell instance
+ {
+ mDictSecondary = dict_list;
+
+ std::string dict_language = mDictLanguage;
+ initHunspell(dict_language);
+ }
+ else if (end_added != dict_add.begin()) // Add the new secondary dictionaries one by one
+ {
+ const std::string app_path = getDictionaryAppPath();
+ const std::string user_path = getDictionaryUserPath();
+ for (dict_list_t::const_iterator it_added = dict_add.begin(); it_added != end_added; ++it_added)
+ {
+ const LLSD dict_entry = getDictionaryData(*it_added);
+ if ( (!dict_entry.isDefined()) || (!dict_entry["installed"].asBoolean()) )
+ {
+ continue;
+ }
+
+ const std::string strFileDic = dict_entry["name"].asString() + ".dic";
+ if (gDirUtilp->fileExists(user_path + strFileDic))
+ {
+ mHunspell->add_dic((user_path + strFileDic).c_str());
+ }
+ else if (gDirUtilp->fileExists(app_path + strFileDic))
+ {
+ mHunspell->add_dic((app_path + strFileDic).c_str());
+ }
+ }
+ mDictSecondary = dict_list;
+ sSettingsChangeSignal();
+ }
+}
+
+void LLSpellChecker::initHunspell(const std::string& dict_language)
+{
+ if (mHunspell)
+ {
+ delete mHunspell;
+ mHunspell = NULL;
+ mDictLanguage.clear();
+ mDictFile.clear();
+ mIgnoreList.clear();
+ }
+
+ const LLSD dict_entry = (!dict_language.empty()) ? getDictionaryData(dict_language) : LLSD();
+ if ( (!dict_entry.isDefined()) || (!dict_entry["installed"].asBoolean()) || (!dict_entry["is_primary"].asBoolean()))
+ {
+ sSettingsChangeSignal();
+ return;
+ }
+
+ const std::string app_path = getDictionaryAppPath();
+ const std::string user_path = getDictionaryUserPath();
+ if (dict_entry.has("name"))
+ {
+ const std::string filename_aff = dict_entry["name"].asString() + ".aff";
+ const std::string filename_dic = dict_entry["name"].asString() + ".dic";
+ if ( (gDirUtilp->fileExists(user_path + filename_aff)) && (gDirUtilp->fileExists(user_path + filename_dic)) )
+ {
+ mHunspell = new Hunspell((user_path + filename_aff).c_str(), (user_path + filename_dic).c_str());
+ }
+ else if ( (gDirUtilp->fileExists(app_path + filename_aff)) && (gDirUtilp->fileExists(app_path + filename_dic)) )
+ {
+ mHunspell = new Hunspell((app_path + filename_aff).c_str(), (app_path + filename_dic).c_str());
+ }
+ if (!mHunspell)
+ {
+ return;
+ }
+
+ mDictLanguage = dict_language;
+ mDictFile = dict_entry["name"].asString();
+
+ if (gDirUtilp->fileExists(user_path + DICT_FILE_CUSTOM))
+ {
+ mHunspell->add_dic((user_path + DICT_FILE_CUSTOM).c_str());
+ }
+
+ if (gDirUtilp->fileExists(user_path + DICT_FILE_IGNORE))
+ {
+ llifstream file_in((user_path + DICT_FILE_IGNORE).c_str(), std::ios::in);
+ if (file_in.is_open())
+ {
+ std::string word; int idxLine = 0;
+ while (getline(file_in, word))
+ {
+ // Skip over the first line since that's just a line count
+ if (0 != idxLine)
+ {
+ LLStringUtil::toLower(word);
+ mIgnoreList.push_back(word);
+ }
+ idxLine++;
+ }
+ }
+ }
+
+ for (dict_list_t::const_iterator it = mDictSecondary.begin(); it != mDictSecondary.end(); ++it)
+ {
+ const LLSD dict_entry = getDictionaryData(*it);
+ if ( (!dict_entry.isDefined()) || (!dict_entry["installed"].asBoolean()) )
+ {
+ continue;
+ }
+
+ const std::string filename_dic = dict_entry["name"].asString() + ".dic";
+ if (gDirUtilp->fileExists(user_path + filename_dic))
+ {
+ mHunspell->add_dic((user_path + filename_dic).c_str());
+ }
+ else if (gDirUtilp->fileExists(app_path + filename_dic))
+ {
+ mHunspell->add_dic((app_path + filename_dic).c_str());
+ }
+ }
+ }
+
+ sSettingsChangeSignal();
+}
+
+// static
+const std::string LLSpellChecker::getDictionaryAppPath()
+{
+ std::string dict_path = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, DICT_DIR, "");
+ return dict_path;
+}
+
+// static
+const std::string LLSpellChecker::getDictionaryUserPath()
+{
+ std::string dict_path = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, DICT_DIR, "");
+ if (!gDirUtilp->fileExists(dict_path))
+ {
+ LLFile::mkdir(dict_path);
+ }
+ return dict_path;
+}
+
+// static
+bool LLSpellChecker::getUseSpellCheck()
+{
+ return (LLSpellChecker::instanceExists()) && (LLSpellChecker::instance().mHunspell);
+}
+
+// static
+bool LLSpellChecker::canRemoveDictionary(const std::string& dict_language)
+{
+ // Only user-installed inactive dictionaries can be removed
+ const LLSD dict_info = getDictionaryData(dict_language);
+ return
+ (dict_info["user_installed"].asBoolean()) &&
+ ( (!getUseSpellCheck()) || (!LLSpellChecker::instance().isActiveDictionary(dict_language)) );
+}
+
+// static
+void LLSpellChecker::removeDictionary(const std::string& dict_language)
+{
+ if (!canRemoveDictionary(dict_language))
+ {
+ return;
+ }
+
+ LLSD dict_map = loadUserDictionaryMap();
+ for (LLSD::array_const_iterator it = dict_map.beginArray(); it != dict_map.endArray(); ++it)
+ {
+ const LLSD& dict_info = *it;
+ if (dict_info["language"].asString() == dict_language)
+ {
+ const std::string dict_dic = getDictionaryUserPath() + dict_info["name"].asString() + ".dic";
+ if (gDirUtilp->fileExists(dict_dic))
+ {
+ LLFile::remove(dict_dic);
+ }
+ const std::string dict_aff = getDictionaryUserPath() + dict_info["name"].asString() + ".aff";
+ if (gDirUtilp->fileExists(dict_aff))
+ {
+ LLFile::remove(dict_aff);
+ }
+ dict_map.erase(it - dict_map.beginArray());
+ break;
+ }
+ }
+ saveUserDictionaryMap(dict_map);
+
+ refreshDictionaryMap();
+}
+
+// static
+LLSD LLSpellChecker::loadUserDictionaryMap()
+{
+ LLSD dict_map;
+ std::string dict_filename(getDictionaryUserPath() + DICT_FILE_USER);
+ llifstream dict_file(dict_filename.c_str(), std::ios::binary);
+ if (dict_file.is_open())
+ {
+ LLSDSerialize::fromXMLDocument(dict_map, dict_file);
+ dict_file.close();
+ }
+ return dict_map;
+}
+
+// static
+void LLSpellChecker::saveUserDictionaryMap(const LLSD& dict_map)
+{
+ llofstream dict_file((getDictionaryUserPath() + DICT_FILE_USER).c_str(), std::ios::trunc);
+ if (dict_file.is_open())
+ {
+ LLSDSerialize::toPrettyXML(dict_map, dict_file);
+ dict_file.close();
+ }
+}
+
+// static
+boost::signals2::connection LLSpellChecker::setSettingsChangeCallback(const settings_change_signal_t::slot_type& cb)
+{
+ return sSettingsChangeSignal.connect(cb);
+}
+
+// static
+void LLSpellChecker::setUseSpellCheck(const std::string& dict_language)
+{
+ if ( (((dict_language.empty()) && (getUseSpellCheck())) || (!dict_language.empty())) &&
+ (LLSpellChecker::instance().mDictLanguage != dict_language) )
+ {
+ LLSpellChecker::instance().initHunspell(dict_language);
+ }
+}
+
+// static
+void LLSpellChecker::initClass()
+{
+ if (sDictMap.isUndefined())
+ {
+ refreshDictionaryMap();
+ }
+}
diff --git a/indra/llui/llspellcheck.h b/indra/llui/llspellcheck.h
new file mode 100755
index 0000000000..4ab80195ea
--- /dev/null
+++ b/indra/llui/llspellcheck.h
@@ -0,0 +1,93 @@
+/**
+ * @file llspellcheck.h
+ * @brief Spell checking functionality
+ *
+ * $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 LLSPELLCHECK_H
+#define LLSPELLCHECK_H
+
+#include "llsingleton.h"
+#include "llui.h"
+#include <boost/signals2.hpp>
+
+class Hunspell;
+
+class LLSpellChecker : public LLSingleton<LLSpellChecker>, public LLInitClass<LLSpellChecker>
+{
+ friend class LLSingleton<LLSpellChecker>;
+ friend class LLInitClass<LLSpellChecker>;
+protected:
+ LLSpellChecker();
+ ~LLSpellChecker();
+
+public:
+ void addToCustomDictionary(const std::string& word);
+ void addToIgnoreList(const std::string& word);
+ bool checkSpelling(const std::string& word) const;
+ S32 getSuggestions(const std::string& word, std::vector<std::string>& suggestions) const;
+protected:
+ void addToDictFile(const std::string& dict_path, const std::string& word);
+ void initHunspell(const std::string& dict_language);
+
+public:
+ typedef std::list<std::string> dict_list_t;
+
+ const std::string& getPrimaryDictionary() const { return mDictLanguage; }
+ const dict_list_t& getSecondaryDictionaries() const { return mDictSecondary; }
+ bool isActiveDictionary(const std::string& dict_language) const;
+ void setSecondaryDictionaries(dict_list_t dict_list);
+
+ static bool canRemoveDictionary(const std::string& dict_language);
+ static const std::string getDictionaryAppPath();
+ static const std::string getDictionaryUserPath();
+ static const LLSD getDictionaryData(const std::string& dict_language);
+ static const LLSD& getDictionaryMap() { return sDictMap; }
+ static bool getUseSpellCheck();
+ static bool hasDictionary(const std::string& dict_language, bool check_installed = false);
+ static void refreshDictionaryMap();
+ static void removeDictionary(const std::string& dict_language);
+ static void setUseSpellCheck(const std::string& dict_language);
+protected:
+ static LLSD loadUserDictionaryMap();
+ static void setDictionaryData(const LLSD& dict_info);
+ static void saveUserDictionaryMap(const LLSD& dict_map);
+
+public:
+ typedef boost::signals2::signal<void()> settings_change_signal_t;
+ static boost::signals2::connection setSettingsChangeCallback(const settings_change_signal_t::slot_type& cb);
+protected:
+ static void initClass();
+
+protected:
+ Hunspell* mHunspell;
+ std::string mDictLanguage;
+ std::string mDictFile;
+ dict_list_t mDictSecondary;
+ std::vector<std::string> mIgnoreList;
+
+ static LLSD sDictMap;
+ static settings_change_signal_t sSettingsChangeSignal;
+};
+
+#endif // LLSPELLCHECK_H
diff --git a/indra/llui/llspellcheckmenuhandler.h b/indra/llui/llspellcheckmenuhandler.h
new file mode 100755
index 0000000000..d5c95bad39
--- /dev/null
+++ b/indra/llui/llspellcheckmenuhandler.h
@@ -0,0 +1,46 @@
+/**
+ * @file llspellcheckmenuhandler.h
+ * @brief Interface used by spell check menu handling
+ *
+ * $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 LLSPELLCHECKMENUHANDLER_H
+#define LLSPELLCHECKMENUHANDLER_H
+
+class LLSpellCheckMenuHandler
+{
+public:
+ virtual bool getSpellCheck() const { return false; }
+
+ virtual const std::string& getSuggestion(U32 index) const { return LLStringUtil::null; }
+ virtual U32 getSuggestionCount() const { return 0; }
+ virtual void replaceWithSuggestion(U32 index){}
+
+ virtual void addToDictionary() {}
+ virtual bool canAddToDictionary() const { return false; }
+
+ virtual void addToIgnore() {}
+ virtual bool canAddToIgnore() const { return false; }
+};
+
+#endif // LLSPELLCHECKMENUHANDLER_H
diff --git a/indra/llui/llspinctrl.cpp b/indra/llui/llspinctrl.cpp
index 6b4e9cf923..8b1ba406c8 100644..100755
--- a/indra/llui/llspinctrl.cpp
+++ b/indra/llui/llspinctrl.cpp
@@ -44,7 +44,7 @@
#include "llresmgr.h"
#include "lluictrlfactory.h"
-const U32 MAX_STRING_LENGTH = 32;
+const U32 MAX_STRING_LENGTH = 255;
static LLDefaultChildRegistry::Register<LLSpinCtrl> r2("spinner");
@@ -52,6 +52,8 @@ LLSpinCtrl::Params::Params()
: label_width("label_width"),
decimal_digits("decimal_digits"),
allow_text_entry("allow_text_entry", true),
+ allow_digits_only("allow_digits_only", false),
+ label_wrap("label_wrap", false),
text_enabled_color("text_enabled_color"),
text_disabled_color("text_disabled_color"),
up_button("up_button"),
@@ -80,6 +82,7 @@ LLSpinCtrl::LLSpinCtrl(const LLSpinCtrl::Params& p)
{
LLRect label_rect( 0, centered_top, label_width, centered_bottom );
LLTextBox::Params params;
+ params.wrap(p.label_wrap);
params.name("SpinCtrl Label");
params.rect(label_rect);
params.initial_value(p.label());
@@ -122,22 +125,20 @@ LLSpinCtrl::LLSpinCtrl(const LLSpinCtrl::Params& p)
params.max_length.bytes(MAX_STRING_LENGTH);
params.commit_callback.function((boost::bind(&LLSpinCtrl::onEditorCommit, this, _2)));
- if( mPrecision>0 )//should accept float numbers
- {
- params.prevalidate_callback(&LLTextValidate::validateFloat);
- }
- else //should accept int numbers
- {
- params.prevalidate_callback(&LLTextValidate::validateInt);
- }
+ //*NOTE: allow entering of any chars for LLCalc, proper input will be evaluated on commit
params.follows.flags(FOLLOWS_LEFT | FOLLOWS_BOTTOM);
mEditor = LLUICtrlFactory::create<LLLineEditor> (params);
mEditor->setFocusReceivedCallback( boost::bind(&LLSpinCtrl::onEditorGainFocus, _1, this ));
+ if (p.allow_digits_only)
+ {
+ mEditor->setPrevalidateInput(LLTextValidate::validateNonNegativeS32NoSpace);
+ }
//RN: this seems to be a BAD IDEA, as it makes the editor behavior different when it has focus
// than when it doesn't. Instead, if you always have to double click to select all the text,
// it's easier to understand
//mEditor->setSelectAllonFocusReceived(TRUE);
+ mEditor->setSelectAllonCommit(FALSE);
addChild(mEditor);
updateEditor();
@@ -152,7 +153,7 @@ F32 clamp_precision(F32 value, S32 decimal_precision)
for (S32 i = 0; i < decimal_precision; i++)
clamped_value *= 10.0;
- clamped_value = llround((F32)clamped_value);
+ clamped_value = ll_round((F32)clamped_value);
for (S32 i = 0; i < decimal_precision; i++)
clamped_value /= 10.0;
@@ -302,9 +303,10 @@ void LLSpinCtrl::onEditorCommit( const LLSD& data )
{
BOOL success = FALSE;
- std::string text = mEditor->getText();
- if( LLLineEditor::postvalidateFloat( text ) )
+ if( mEditor->evaluateFloat() )
{
+ std::string text = mEditor->getText();
+
LLLocale locale(LLLocale::USER_LOCALE);
F32 val = (F32) atof(text.c_str());
@@ -325,7 +327,11 @@ void LLSpinCtrl::onEditorCommit( const LLSD& data )
}
updateEditor();
- if( !success )
+ if( success )
+ {
+ updateEditor();
+ }
+ else
{
reportInvalidData();
}
@@ -378,7 +384,7 @@ void LLSpinCtrl::setPrecision(S32 precision)
{
if (precision < 0 || precision > 10)
{
- llerrs << "LLSpinCtrl::setPrecision - precision out of range" << llendl;
+ LL_ERRS() << "LLSpinCtrl::setPrecision - precision out of range" << LL_ENDL;
return;
}
@@ -394,7 +400,7 @@ void LLSpinCtrl::setLabel(const LLStringExplicit& label)
}
else
{
- llwarns << "Attempting to set label on LLSpinCtrl constructed without one " << getName() << llendl;
+ LL_WARNS() << "Attempting to set label on LLSpinCtrl constructed without one " << getName() << LL_ENDL;
}
updateLabelColor();
}
diff --git a/indra/llui/llspinctrl.h b/indra/llui/llspinctrl.h
index 8960971594..e34add879d 100644..100755
--- a/indra/llui/llspinctrl.h
+++ b/indra/llui/llspinctrl.h
@@ -44,6 +44,8 @@ public:
Optional<S32> label_width;
Optional<U32> decimal_digits;
Optional<bool> allow_text_entry;
+ Optional<bool> allow_digits_only;
+ Optional<bool> label_wrap;
Optional<LLUIColor> text_enabled_color;
Optional<LLUIColor> text_disabled_color;
@@ -95,6 +97,9 @@ public:
void onUpBtn(const LLSD& data);
void onDownBtn(const LLSD& data);
+
+ const LLColor4& getEnabledTextColor() const { return mTextEnabledColor.get(); }
+ const LLColor4& getDisabledTextColor() const { return mTextDisabledColor.get(); }
private:
void updateLabelColor();
diff --git a/indra/llui/llstatbar.cpp b/indra/llui/llstatbar.cpp
index ec4db14790..35f5330a3f 100644..100755
--- a/indra/llui/llstatbar.cpp
+++ b/indra/llui/llstatbar.cpp
@@ -34,234 +34,560 @@
#include "llgl.h"
#include "llfontgl.h"
-#include "llstat.h"
#include "lluictrlfactory.h"
+#include "lltracerecording.h"
+#include "llcriticaldamp.h"
+#include "lltooltip.h"
+#include "lllocalcliprect.h"
+#include <iostream>
+
+// rate at which to update display of value that is rapidly changing
+const F32 MEAN_VALUE_UPDATE_TIME = 1.f / 4.f;
+// time between value changes that qualifies as a "rapid change"
+const F32Seconds RAPID_CHANGE_THRESHOLD(0.2f);
+// maximum number of rapid changes in RAPID_CHANGE_WINDOW before switching over to displaying the mean
+// instead of latest value
+const S32 MAX_RAPID_CHANGES_PER_SEC = 10;
+// period of time over which to measure rapid changes
+const F32Seconds RAPID_CHANGE_WINDOW(1.f);
+
+F32 calc_tick_value(F32 min, F32 max)
+{
+ F32 range = max - min;
+ const S32 DIVISORS[] = {6, 8, 10, 4, 5};
+ // try storing
+ S32 best_decimal_digit_count = S32_MAX;
+ S32 best_divisor = 10;
+ for (U32 divisor_idx = 0; divisor_idx < LL_ARRAY_SIZE(DIVISORS); divisor_idx++)
+ {
+ S32 divisor = DIVISORS[divisor_idx];
+ F32 possible_tick_value = range / divisor;
+ S32 num_whole_digits = llceil(logf(llabs(min + possible_tick_value)) * OO_LN10);
+ for (S32 digit_count = -(num_whole_digits - 1); digit_count < 6; digit_count++)
+ {
+ F32 test_tick_value = min + (possible_tick_value * pow(10.0, digit_count));
-///////////////////////////////////////////////////////////////////////////////////
+ if (is_approx_equal((F32)(S32)test_tick_value, test_tick_value))
+ {
+ if (digit_count < best_decimal_digit_count)
+ {
+ best_decimal_digit_count = digit_count;
+ best_divisor = divisor;
+ }
+ break;
+ }
+ }
+ }
-LLStatBar::LLStatBar(const Params& p)
- : LLView(p),
- mLabel(p.label),
- mUnitLabel(p.unit_label),
- mMinBar(p.bar_min),
- mMaxBar(p.bar_max),
- mStatp(LLStat::getStat(p.stat)),
- mTickSpacing(p.tick_spacing),
- mLabelSpacing(p.label_spacing),
- mPrecision(p.precision),
- mUpdatesPerSec(p.update_rate),
- mPerSec(p.show_per_sec),
- mDisplayBar(p.show_bar),
- mDisplayHistory(p.show_history),
- mDisplayMean(p.show_mean)
-{
+ return is_approx_equal(range, 0.f) ? 0.f : range / best_divisor;
}
-BOOL LLStatBar::handleMouseDown(S32 x, S32 y, MASK mask)
+void calc_auto_scale_range(F32& min, F32& max, F32& tick)
{
- if (mDisplayBar)
+ min = llmin(0.f, min, max);
+ max = llmax(0.f, min, max);
+
+ const F32 RANGES[] = {0.f, 1.f, 1.5f, 2.f, 3.f, 5.f, 10.f};
+ const F32 TICKS[] = {0.f, 0.25f, 0.5f, 1.f, 1.f, 1.f, 2.f };
+
+ const S32 num_digits_max = is_approx_equal(llabs(max), 0.f)
+ ? S32_MIN + 1
+ : llceil(logf(llabs(max)) * OO_LN10);
+ const S32 num_digits_min = is_approx_equal(llabs(min), 0.f)
+ ? S32_MIN + 1
+ : llceil(logf(llabs(min)) * OO_LN10);
+
+ const S32 num_digits = llmax(num_digits_max, num_digits_min);
+ const F32 power_of_10 = pow(10.0, num_digits - 1);
+ const F32 starting_max = power_of_10 * ((max < 0.f) ? -1 : 1);
+ const F32 starting_min = power_of_10 * ((min < 0.f) ? -1 : 1);
+
+ F32 cur_max = starting_max;
+ F32 cur_min = starting_min;
+ F32 out_max = max;
+ F32 out_min = min;
+
+ F32 cur_tick_min = 0.f;
+ F32 cur_tick_max = 0.f;
+
+ for (S32 range_idx = 0; range_idx < LL_ARRAY_SIZE(RANGES); range_idx++)
{
- if (mDisplayHistory)
+ cur_max = starting_max * RANGES[range_idx];
+ cur_min = starting_min * RANGES[range_idx];
+
+ if (min > 0.f && cur_min <= min)
{
- mDisplayBar = FALSE;
- mDisplayHistory = FALSE;
+ out_min = cur_min;
+ cur_tick_min = TICKS[range_idx];
}
- else
+ if (max < 0.f && cur_max >= max)
{
- mDisplayHistory = TRUE;
+ out_max = cur_max;
+ cur_tick_max = TICKS[range_idx];
}
}
- else
+
+ cur_max = starting_max;
+ cur_min = starting_min;
+ for (S32 range_idx = LL_ARRAY_SIZE(RANGES) - 1; range_idx >= 0; range_idx--)
{
- mDisplayBar = TRUE;
+ cur_max = starting_max * RANGES[range_idx];
+ cur_min = starting_min * RANGES[range_idx];
+
+ if (min < 0.f && cur_min <= min)
+ {
+ out_min = cur_min;
+ cur_tick_min = TICKS[range_idx];
+ }
+ if (max > 0.f && cur_max >= max)
+ {
+ out_max = cur_max;
+ cur_tick_max = TICKS[range_idx];
+ }
}
- LLView* parent = getParent();
- parent->reshape(parent->getRect().getWidth(), parent->getRect().getHeight(), FALSE);
+ tick = power_of_10 * llmax(cur_tick_min, cur_tick_max);
+ min = out_min;
+ max = out_max;
+}
- return FALSE;
+LLStatBar::Params::Params()
+: label("label"),
+ unit_label("unit_label"),
+ bar_min("bar_min", 0.f),
+ bar_max("bar_max", 0.f),
+ tick_spacing("tick_spacing", 0.f),
+ decimal_digits("decimal_digits", 3),
+ show_bar("show_bar", false),
+ show_history("show_history", false),
+ scale_range("scale_range", true),
+ num_frames("num_frames", 200),
+ num_frames_short("num_frames_short", 20),
+ max_height("max_height", 100),
+ stat("stat"),
+ orientation("orientation", VERTICAL)
+{
+ changeDefault(follows.flags, FOLLOWS_TOP | FOLLOWS_LEFT);
}
-void LLStatBar::draw()
+///////////////////////////////////////////////////////////////////////////////////
+
+LLStatBar::LLStatBar(const Params& p)
+: LLView(p),
+ mLabel(p.label),
+ mUnitLabel(p.unit_label),
+ mTargetMinBar(llmin(p.bar_min, p.bar_max)),
+ mTargetMaxBar(llmax(p.bar_max, p.bar_min)),
+ mCurMaxBar(p.bar_max),
+ mCurMinBar(0),
+ mDecimalDigits(p.decimal_digits),
+ mNumHistoryFrames(p.num_frames),
+ mNumShortHistoryFrames(p.num_frames_short),
+ mMaxHeight(p.max_height),
+ mDisplayBar(p.show_bar),
+ mDisplayHistory(p.show_history),
+ mOrientation(p.orientation),
+ mAutoScaleMax(!p.bar_max.isProvided()),
+ mAutoScaleMin(!p.bar_min.isProvided()),
+ mTickSpacing(p.tick_spacing),
+ mLastDisplayValue(0.f),
+ mStatType(STAT_NONE)
{
- if (!mStatp)
- {
-// llinfos << "No stats for statistics bar!" << llendl;
- return;
- }
+ mFloatingTargetMinBar = mTargetMinBar;
+ mFloatingTargetMaxBar = mTargetMaxBar;
- // Get the values.
- F32 current, min, max, mean;
- if (mPerSec)
+ mStat.valid = NULL;
+ // tick value will be automatically calculated later
+ if (!p.tick_spacing.isProvided() && p.bar_min.isProvided() && p.bar_max.isProvided())
{
- current = mStatp->getCurrentPerSec();
- min = mStatp->getMinPerSec();
- max = mStatp->getMaxPerSec();
- mean = mStatp->getMeanPerSec();
+ mTickSpacing = calc_tick_value(mTargetMinBar, mTargetMaxBar);
}
- else
+
+ setStat(p.stat);
+}
+
+BOOL LLStatBar::handleHover(S32 x, S32 y, MASK mask)
+{
+ switch(mStatType)
{
- current = mStatp->getCurrent();
- min = mStatp->getMin();
- max = mStatp->getMax();
- mean = mStatp->getMean();
+ case STAT_COUNT:
+ LLToolTipMgr::instance().show(LLToolTip::Params().message(mStat.countStatp->getDescription()).sticky_rect(calcScreenRect()));
+ break;
+ case STAT_EVENT:
+ LLToolTipMgr::instance().show(LLToolTip::Params().message(mStat.eventStatp->getDescription()).sticky_rect(calcScreenRect()));
+ break;
+ case STAT_SAMPLE:
+ LLToolTipMgr::instance().show(LLToolTip::Params().message(mStat.sampleStatp->getDescription()).sticky_rect(calcScreenRect()));
+ break;
+ case STAT_MEM:
+ LLToolTipMgr::instance().show(LLToolTip::Params().message(mStat.memStatp->getDescription()).sticky_rect(calcScreenRect()));
+ break;
+ default:
+ break;
}
+ return TRUE;
+}
-
- if ((mUpdatesPerSec == 0.f) || (mUpdateTimer.getElapsedTimeF32() > 1.f/mUpdatesPerSec) || (mValue == 0.f))
+BOOL LLStatBar::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = LLView::handleMouseDown(x, y, mask);
+ if (!handled)
{
- if (mDisplayMean)
+ if (mDisplayBar)
{
- mValue = mean;
+ if (mDisplayHistory || mOrientation == HORIZONTAL)
+ {
+ mDisplayBar = FALSE;
+ mDisplayHistory = FALSE;
+ }
+ else
+ {
+ mDisplayHistory = TRUE;
+ }
}
else
{
- mValue = current;
+ mDisplayBar = TRUE;
+ if (mOrientation == HORIZONTAL)
+ {
+ mDisplayHistory = TRUE;
+ }
}
- mUpdateTimer.reset();
+ LLView* parent = getParent();
+ parent->reshape(parent->getRect().getWidth(), parent->getRect().getHeight(), FALSE);
}
+ return TRUE;
+}
- S32 width = getRect().getWidth() - 40;
- S32 max_width = width;
- S32 bar_top = getRect().getHeight() - 15; // 16 pixels from top.
- S32 bar_height = bar_top - 20;
- S32 tick_height = 4;
- S32 tick_width = 1;
- S32 left, top, right, bottom;
+template<typename T>
+S32 calc_num_rapid_changes(LLTrace::PeriodicRecording& periodic_recording, const T& stat, const F32Seconds time_period)
+{
+ F32Seconds elapsed_time,
+ time_since_value_changed;
+ S32 num_rapid_changes = 0;
+ const F32Seconds RAPID_CHANGE_THRESHOLD = F32Seconds(0.3f);
+ F64 last_value = periodic_recording.getPrevRecording(1).getLastValue(stat);
- F32 value_scale = max_width/(mMaxBar - mMinBar);
+ for (S32 i = 2; i < periodic_recording.getNumRecordedPeriods(); i++)
+ {
+ LLTrace::Recording& recording = periodic_recording.getPrevRecording(i);
+ F64 cur_value = recording.getLastValue(stat);
- LLFontGL::getFontMonospace()->renderUTF8(mLabel, 0, 0, getRect().getHeight(), LLColor4(1.f, 1.f, 1.f, 1.f),
- LLFontGL::LEFT, LLFontGL::TOP);
+ if (last_value != cur_value)
+ {
+ if (time_since_value_changed < RAPID_CHANGE_THRESHOLD) num_rapid_changes++;
+ time_since_value_changed = (F32Seconds)0;
+ }
+ last_value = cur_value;
+
+ elapsed_time += recording.getDuration();
+ if (elapsed_time > time_period) break;
+ }
- std::string value_format;
- std::string value_str;
- if (!mUnitLabel.empty())
+ return num_rapid_changes;
+}
+
+void LLStatBar::draw()
+{
+ LLLocalClipRect _(getLocalRect());
+
+ LLTrace::PeriodicRecording& frame_recording = LLTrace::get_frame_recording();
+ LLTrace::Recording& last_frame_recording = frame_recording.getLastRecording();
+
+ std::string unit_label;
+ F32 current = 0,
+ min = 0,
+ max = 0,
+ mean = 0,
+ display_value = 0;
+ S32 num_frames = mDisplayHistory
+ ? mNumHistoryFrames
+ : mNumShortHistoryFrames;
+ S32 num_rapid_changes = 0;
+ S32 decimal_digits = mDecimalDigits;
+
+ switch(mStatType)
{
- value_format = llformat( "%%.%df%%s", mPrecision);
- value_str = llformat( value_format.c_str(), mValue, mUnitLabel.c_str());
+ case STAT_COUNT:
+ {
+ const LLTrace::StatType<LLTrace::CountAccumulator>& count_stat = *mStat.countStatp;
+
+ unit_label = std::string(count_stat.getUnitLabel()) + "/s";
+ current = last_frame_recording.getPerSec(count_stat);
+ min = frame_recording.getPeriodMinPerSec(count_stat, num_frames);
+ max = frame_recording.getPeriodMaxPerSec(count_stat, num_frames);
+ mean = frame_recording.getPeriodMeanPerSec(count_stat, num_frames);
+ display_value = mean;
+ }
+ break;
+ case STAT_EVENT:
+ {
+ const LLTrace::StatType<LLTrace::EventAccumulator>& event_stat = *mStat.eventStatp;
+
+ unit_label = mUnitLabel.empty() ? event_stat.getUnitLabel() : mUnitLabel;
+ current = last_frame_recording.getLastValue(event_stat);
+ min = frame_recording.getPeriodMin(event_stat, num_frames);
+ max = frame_recording.getPeriodMax(event_stat, num_frames);
+ mean = frame_recording.getPeriodMean(event_stat, num_frames);
+ display_value = mean;
+ }
+ break;
+ case STAT_SAMPLE:
+ {
+ const LLTrace::StatType<LLTrace::SampleAccumulator>& sample_stat = *mStat.sampleStatp;
+
+ unit_label = mUnitLabel.empty() ? sample_stat.getUnitLabel() : mUnitLabel;
+ current = last_frame_recording.getLastValue(sample_stat);
+ min = frame_recording.getPeriodMin(sample_stat, num_frames);
+ max = frame_recording.getPeriodMax(sample_stat, num_frames);
+ mean = frame_recording.getPeriodMean(sample_stat, num_frames);
+ num_rapid_changes = calc_num_rapid_changes(frame_recording, sample_stat, RAPID_CHANGE_WINDOW);
+
+ if (num_rapid_changes / RAPID_CHANGE_WINDOW.value() > MAX_RAPID_CHANGES_PER_SEC)
+ {
+ display_value = mean;
+ }
+ else
+ {
+ display_value = current;
+ // always display current value, don't rate limit
+ mLastDisplayValue = current;
+ if (is_approx_equal((F32)(S32)display_value, display_value))
+ {
+ decimal_digits = 0;
+ }
+ }
+ }
+ break;
+ case STAT_MEM:
+ {
+ const LLTrace::StatType<LLTrace::MemAccumulator>& mem_stat = *mStat.memStatp;
+
+ unit_label = mUnitLabel.empty() ? mem_stat.getUnitLabel() : mUnitLabel;
+ current = last_frame_recording.getLastValue(mem_stat).value();
+ min = frame_recording.getPeriodMin(mem_stat, num_frames).value();
+ max = frame_recording.getPeriodMax(mem_stat, num_frames).value();
+ mean = frame_recording.getPeriodMean(mem_stat, num_frames).value();
+ display_value = current;
+ }
+ break;
+ default:
+ break;
}
- else
+
+ LLRect bar_rect;
+ if (mOrientation == HORIZONTAL)
{
- value_format = llformat( "%%.%df", mPrecision);
- value_str = llformat( value_format.c_str(), mValue);
+ bar_rect.mTop = llmax(5, getRect().getHeight() - 15);
+ bar_rect.mLeft = 0;
+ bar_rect.mRight = getRect().getWidth() - 40;
+ bar_rect.mBottom = llmin(bar_rect.mTop - 5, 0);
+ }
+ else // VERTICAL
+ {
+ bar_rect.mTop = llmax(5, getRect().getHeight() - 15);
+ bar_rect.mLeft = 0;
+ bar_rect.mRight = getRect().getWidth();
+ bar_rect.mBottom = llmin(bar_rect.mTop - 5, 20);
}
- // Draw the value.
- LLFontGL::getFontMonospace()->renderUTF8(value_str, 0, width, getRect().getHeight(),
- LLColor4(1.f, 1.f, 1.f, 0.5f),
- LLFontGL::RIGHT, LLFontGL::TOP);
+ mCurMaxBar = LLSmoothInterpolation::lerp(mCurMaxBar, mTargetMaxBar, 0.05f);
+ mCurMinBar = LLSmoothInterpolation::lerp(mCurMinBar, mTargetMinBar, 0.05f);
- value_format = llformat( "%%.%df", mPrecision);
- if (mDisplayBar)
+ // rate limited updates
+ if (mLastDisplayValueTimer.getElapsedTimeF32() < MEAN_VALUE_UPDATE_TIME)
{
- std::string tick_label;
+ display_value = mLastDisplayValue;
+ }
+ else
+ {
+ mLastDisplayValueTimer.reset();
+ }
+ drawLabelAndValue(display_value, unit_label, bar_rect, decimal_digits);
+ mLastDisplayValue = display_value;
+ if (mDisplayBar && mStat.valid)
+ {
// Draw the tick marks.
- F32 tick_value;
- top = bar_top;
- bottom = bar_top - bar_height - tick_height/2;
-
LLGLSUIDefault gls_ui;
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
- for (tick_value = mMinBar; tick_value <= mMaxBar; tick_value += mTickSpacing)
+ F32 value_scale;
+ if (mCurMaxBar == mCurMinBar)
{
- left = llfloor((tick_value - mMinBar)*value_scale);
- right = left + tick_width;
- gl_rect_2d(left, top, right, bottom, LLColor4(1.f, 1.f, 1.f, 0.1f));
+ value_scale = 0.f;
}
-
- // Draw the tick labels (and big ticks).
- bottom = bar_top - bar_height - tick_height;
- for (tick_value = mMinBar; tick_value <= mMaxBar; tick_value += mLabelSpacing)
+ else
{
- left = llfloor((tick_value - mMinBar)*value_scale);
- right = left + tick_width;
- gl_rect_2d(left, top, right, bottom, LLColor4(1.f, 1.f, 1.f, 0.25f));
-
- tick_label = llformat( value_format.c_str(), tick_value);
- // draw labels for the tick marks
- LLFontGL::getFontMonospace()->renderUTF8(tick_label, 0, left - 1, bar_top - bar_height - tick_height,
- LLColor4(1.f, 1.f, 1.f, 0.5f),
- LLFontGL::LEFT, LLFontGL::TOP);
+ value_scale = (mOrientation == HORIZONTAL)
+ ? (bar_rect.getHeight())/(mCurMaxBar - mCurMinBar)
+ : (bar_rect.getWidth())/(mCurMaxBar - mCurMinBar);
}
- // Now, draw the bars
- top = bar_top;
- bottom = bar_top - bar_height;
+ drawTicks(min, max, value_scale, bar_rect);
// draw background bar.
- left = 0;
- right = width;
- gl_rect_2d(left, top, right, bottom, LLColor4(0.f, 0.f, 0.f, 0.25f));
+ gl_rect_2d(bar_rect.mLeft, bar_rect.mTop, bar_rect.mRight, bar_rect.mBottom, LLColor4(0.f, 0.f, 0.f, 0.25f));
- if (mStatp->getNumValues() == 0)
+ // draw values
+ if (!llisnan(display_value) && frame_recording.getNumRecordedPeriods() != 0)
{
- // No data, don't draw anything...
- return;
- }
- // draw min and max
- left = (S32) ((min - mMinBar) * value_scale);
+ // draw min and max
+ S32 begin = (S32) ((min - mCurMinBar) * value_scale);
- if (left < 0)
- {
- left = 0;
- llwarns << "Min:" << min << llendl;
- }
+ if (begin < 0)
+ {
+ begin = 0;
+ }
- right = (S32) ((max - mMinBar) * value_scale);
- gl_rect_2d(left, top, right, bottom, LLColor4(1.f, 0.f, 0.f, 0.25f));
+ S32 end = (S32) ((max - mCurMinBar) * value_scale);
+ if (mOrientation == HORIZONTAL)
+ {
+ gl_rect_2d(bar_rect.mLeft, end, bar_rect.mRight, begin, LLColor4(1.f, 0.f, 0.f, 0.25f));
+ }
+ else // VERTICAL
+ {
+ gl_rect_2d(begin, bar_rect.mTop, end, bar_rect.mBottom, LLColor4(1.f, 0.f, 0.f, 0.25f));
+ }
- S32 num_values = mStatp->getNumValues() - 1;
- if (mDisplayHistory)
- {
- S32 i;
- for (i = 0; i < num_values; i++)
+ F32 span = (mOrientation == HORIZONTAL)
+ ? (bar_rect.getWidth())
+ : (bar_rect.getHeight());
+
+ if (mDisplayHistory && mStat.valid)
{
- if (i == mStatp->getNextBin())
+ const S32 num_values = frame_recording.getNumRecordedPeriods() - 1;
+ F32 min_value = 0.f,
+ max_value = 0.f;
+
+ gGL.color4f(1.f, 0.f, 0.f, 1.f);
+ gGL.begin( LLRender::QUADS );
+ const S32 max_frame = llmin(num_frames, num_values);
+ U32 num_samples = 0;
+ for (S32 i = 1; i <= max_frame; i++)
{
- continue;
+ F32 offset = ((F32)i / (F32)num_frames) * span;
+ LLTrace::Recording& recording = frame_recording.getPrevRecording(i);
+
+ switch(mStatType)
+ {
+ case STAT_COUNT:
+ min_value = recording.getPerSec(*mStat.countStatp);
+ max_value = min_value;
+ num_samples = recording.getSampleCount(*mStat.countStatp);
+ break;
+ case STAT_EVENT:
+ min_value = recording.getMin(*mStat.eventStatp);
+ max_value = recording.getMax(*mStat.eventStatp);
+ num_samples = recording.getSampleCount(*mStat.eventStatp);
+ break;
+ case STAT_SAMPLE:
+ min_value = recording.getMin(*mStat.sampleStatp);
+ max_value = recording.getMax(*mStat.sampleStatp);
+ num_samples = recording.getSampleCount(*mStat.sampleStatp);
+ break;
+ case STAT_MEM:
+ min_value = recording.getMin(*mStat.memStatp).value();
+ max_value = recording.getMax(*mStat.memStatp).value();
+ num_samples = 1;
+ break;
+ default:
+ break;
+ }
+
+ if (!num_samples) continue;
+
+ F32 min = (min_value - mCurMinBar) * value_scale;
+ F32 max = llmax(min + 1, (max_value - mCurMinBar) * value_scale);
+ if (mOrientation == HORIZONTAL)
+ {
+ gGL.vertex2f((F32)bar_rect.mRight - offset, max);
+ gGL.vertex2f((F32)bar_rect.mRight - offset, min);
+ gGL.vertex2f((F32)bar_rect.mRight - offset - 1, min);
+ gGL.vertex2f((F32)bar_rect.mRight - offset - 1, max);
+ }
+ else
+ {
+ gGL.vertex2f(min, (F32)bar_rect.mBottom + offset + 1);
+ gGL.vertex2f(min, (F32)bar_rect.mBottom + offset);
+ gGL.vertex2f(max, (F32)bar_rect.mBottom + offset);
+ gGL.vertex2f(max, (F32)bar_rect.mBottom + offset + 1 );
+ }
}
- if (mPerSec)
+ gGL.end();
+ }
+ else
+ {
+ S32 begin = (S32) ((current - mCurMinBar) * value_scale) - 1;
+ S32 end = (S32) ((current - mCurMinBar) * value_scale) + 1;
+ // draw current
+ if (mOrientation == HORIZONTAL)
{
- left = (S32)((mStatp->getPrevPerSec(i) - mMinBar) * value_scale);
- right = (S32)((mStatp->getPrevPerSec(i) - mMinBar) * value_scale) + 1;
- gl_rect_2d(left, bottom+i+1, right, bottom+i, LLColor4(1.f, 0.f, 0.f, 1.f));
+ gl_rect_2d(bar_rect.mLeft, end, bar_rect.mRight, begin, LLColor4(1.f, 0.f, 0.f, 1.f));
}
else
{
- left = (S32)((mStatp->getPrev(i) - mMinBar) * value_scale);
- right = (S32)((mStatp->getPrev(i) - mMinBar) * value_scale) + 1;
- gl_rect_2d(left, bottom+i+1, right, bottom+i, LLColor4(1.f, 0.f, 0.f, 1.f));
+ gl_rect_2d(begin, bar_rect.mTop, end, bar_rect.mBottom, LLColor4(1.f, 0.f, 0.f, 1.f));
}
}
- }
- else
- {
- // draw current
- left = (S32) ((current - mMinBar) * value_scale) - 1;
- right = (S32) ((current - mMinBar) * value_scale) + 1;
- gl_rect_2d(left, top, right, bottom, LLColor4(1.f, 0.f, 0.f, 1.f));
- }
- // draw mean bar
- top = bar_top + 2;
- bottom = bar_top - bar_height - 2;
- left = (S32) ((mean - mMinBar) * value_scale) - 1;
- right = (S32) ((mean - mMinBar) * value_scale) + 1;
- gl_rect_2d(left, top, right, bottom, LLColor4(0.f, 1.f, 0.f, 1.f));
+ // draw mean bar
+ {
+ const S32 begin = (S32) ((mean - mCurMinBar) * value_scale) - 1;
+ const S32 end = (S32) ((mean - mCurMinBar) * value_scale) + 1;
+ if (mOrientation == HORIZONTAL)
+ {
+ gl_rect_2d(bar_rect.mLeft - 2, begin, bar_rect.mRight + 2, end, LLColor4(0.f, 1.f, 0.f, 1.f));
+ }
+ else
+ {
+ gl_rect_2d(begin, bar_rect.mTop + 2, end, bar_rect.mBottom - 2, LLColor4(0.f, 1.f, 0.f, 1.f));
+ }
+ }
+ }
}
LLView::draw();
}
-void LLStatBar::setRange(F32 bar_min, F32 bar_max, F32 tick_spacing, F32 label_spacing)
+void LLStatBar::setStat(const std::string& stat_name)
+{
+ using namespace LLTrace;
+ const StatType<CountAccumulator>* count_stat;
+ const StatType<EventAccumulator>* event_stat;
+ const StatType<SampleAccumulator>* sample_stat;
+ const StatType<MemAccumulator>* mem_stat;
+
+ if ((count_stat = StatType<CountAccumulator>::getInstance(stat_name)))
+ {
+ mStat.countStatp = count_stat;
+ mStatType = STAT_COUNT;
+ }
+ else if ((event_stat = StatType<EventAccumulator>::getInstance(stat_name)))
+ {
+ mStat.eventStatp = event_stat;
+ mStatType = STAT_EVENT;
+ }
+ else if ((sample_stat = StatType<SampleAccumulator>::getInstance(stat_name)))
+ {
+ mStat.sampleStatp = sample_stat;
+ mStatType = STAT_SAMPLE;
+ }
+ else if ((mem_stat = StatType<MemAccumulator>::getInstance(stat_name)))
+ {
+ mStat.memStatp = mem_stat;
+ mStatType = STAT_MEM;
+ }
+}
+
+
+void LLStatBar::setRange(F32 bar_min, F32 bar_max)
{
- mMinBar = bar_min;
- mMaxBar = bar_max;
- mTickSpacing = tick_spacing;
- mLabelSpacing = label_spacing;
+ mTargetMinBar = llmin(bar_min, bar_max);
+ mTargetMaxBar = llmax(bar_min, bar_max);
+ mFloatingTargetMinBar = mTargetMinBar;
+ mFloatingTargetMaxBar = mTargetMaxBar;
+ mTickSpacing = calc_tick_value(mTargetMinBar, mTargetMaxBar);
}
LLRect LLStatBar::getRequiredRect()
@@ -272,7 +598,7 @@ LLRect LLStatBar::getRequiredRect()
{
if (mDisplayHistory)
{
- rect.mTop = 67;
+ rect.mTop = mMaxHeight;
}
else
{
@@ -285,3 +611,122 @@ LLRect LLStatBar::getRequiredRect()
}
return rect;
}
+
+void LLStatBar::drawLabelAndValue( F32 value, std::string &label, LLRect &bar_rect, S32 decimal_digits )
+{
+ LLFontGL::getFontMonospace()->renderUTF8(mLabel, 0, 0, getRect().getHeight(), LLColor4(1.f, 1.f, 1.f, 1.f),
+ LLFontGL::LEFT, LLFontGL::TOP);
+
+ std::string value_str = !llisnan(value)
+ ? llformat("%10.*f %s", decimal_digits, value, label.c_str())
+ : "n/a";
+
+ // Draw the current value.
+ if (mOrientation == HORIZONTAL)
+ {
+ LLFontGL::getFontMonospace()->renderUTF8(value_str, 0, bar_rect.mRight, getRect().getHeight(),
+ LLColor4(1.f, 1.f, 1.f, 0.5f),
+ LLFontGL::RIGHT, LLFontGL::TOP);
+ }
+ else
+ {
+ LLFontGL::getFontMonospace()->renderUTF8(value_str, 0, bar_rect.mRight, getRect().getHeight(),
+ LLColor4(1.f, 1.f, 1.f, 0.5f),
+ LLFontGL::RIGHT, LLFontGL::TOP);
+ }
+}
+
+void LLStatBar::drawTicks( F32 min, F32 max, F32 value_scale, LLRect &bar_rect )
+{
+ if (!llisnan(min) && (mAutoScaleMax || mAutoScaleMin))
+ {
+ F32 u = LLSmoothInterpolation::getInterpolant(10.f);
+ mFloatingTargetMinBar = llmin(min, lerp(mFloatingTargetMinBar, min, u));
+ mFloatingTargetMaxBar = llmax(max, lerp(mFloatingTargetMaxBar, max, u));
+ F32 range_min = mAutoScaleMin ? mFloatingTargetMinBar : mTargetMinBar;
+ F32 range_max = mAutoScaleMax ? mFloatingTargetMaxBar : mTargetMaxBar;
+ F32 tick_value = 0.f;
+ calc_auto_scale_range(range_min, range_max, tick_value);
+ if (mAutoScaleMin) { mTargetMinBar = range_min; }
+ if (mAutoScaleMax) { mTargetMaxBar = range_max; }
+ if (mAutoScaleMin && mAutoScaleMax)
+ {
+ mTickSpacing = tick_value;
+ }
+ else
+ {
+ mTickSpacing = calc_tick_value(mTargetMinBar, mTargetMaxBar);
+ }
+ }
+
+ // start counting from actual min, not current, animating min, so that ticks don't float between numbers
+ // ensure ticks always hit 0
+ S32 last_tick = S32_MIN;
+ S32 last_label = S32_MIN;
+ if (mTickSpacing > 0.f && value_scale > 0.f)
+ {
+ const S32 MIN_TICK_SPACING = mOrientation == HORIZONTAL ? 20 : 30;
+ const S32 MIN_LABEL_SPACING = mOrientation == HORIZONTAL ? 30 : 60;
+ const S32 TICK_LENGTH = 4;
+ const S32 TICK_WIDTH = 1;
+
+ F32 start = mCurMinBar < 0.f
+ ? llceil(-mCurMinBar / mTickSpacing) * -mTickSpacing
+ : 0.f;
+ for (F32 tick_value = start; ;tick_value += mTickSpacing)
+ {
+ // clamp to S32_MAX / 2 to avoid floating point to integer overflow resulting in S32_MIN
+ const S32 tick_begin = llfloor(llmin((F32)(S32_MAX / 2), (tick_value - mCurMinBar)*value_scale));
+ const S32 tick_end = tick_begin + TICK_WIDTH;
+ if (tick_begin < last_tick + MIN_TICK_SPACING)
+ {
+ continue;
+ }
+ last_tick = tick_begin;
+
+ S32 decimal_digits = mDecimalDigits;
+ if (is_approx_equal((F32)(S32)tick_value, tick_value))
+ {
+ decimal_digits = 0;
+ }
+ std::string tick_label = llformat("%.*f", decimal_digits, tick_value);
+ S32 tick_label_width = LLFontGL::getFontMonospace()->getWidth(tick_label);
+ if (mOrientation == HORIZONTAL)
+ {
+ if (tick_begin > last_label + MIN_LABEL_SPACING)
+ {
+ gl_rect_2d(bar_rect.mLeft, tick_end, bar_rect.mRight - TICK_LENGTH, tick_begin, LLColor4(1.f, 1.f, 1.f, 0.25f));
+ LLFontGL::getFontMonospace()->renderUTF8(tick_label, 0, bar_rect.mRight, tick_begin,
+ LLColor4(1.f, 1.f, 1.f, 0.5f),
+ LLFontGL::LEFT, LLFontGL::VCENTER);
+ last_label = tick_begin;
+ }
+ else
+ {
+ gl_rect_2d(bar_rect.mLeft, tick_end, bar_rect.mRight - TICK_LENGTH/2, tick_begin, LLColor4(1.f, 1.f, 1.f, 0.1f));
+ }
+ }
+ else
+ {
+ if (tick_begin > last_label + MIN_LABEL_SPACING)
+ {
+ gl_rect_2d(tick_begin, bar_rect.mTop, tick_end, bar_rect.mBottom - TICK_LENGTH, LLColor4(1.f, 1.f, 1.f, 0.25f));
+ S32 label_pos = tick_begin - ll_round((F32)tick_label_width * ((F32)tick_begin / (F32)bar_rect.getWidth()));
+ LLFontGL::getFontMonospace()->renderUTF8(tick_label, 0, label_pos, bar_rect.mBottom - TICK_LENGTH,
+ LLColor4(1.f, 1.f, 1.f, 0.5f),
+ LLFontGL::LEFT, LLFontGL::TOP);
+ last_label = label_pos;
+ }
+ else
+ {
+ gl_rect_2d(tick_begin, bar_rect.mTop, tick_end, bar_rect.mBottom - TICK_LENGTH/2, LLColor4(1.f, 1.f, 1.f, 0.1f));
+ }
+ }
+ // always draw one tick value past tick_end, so we can see part of the text, if possible
+ if (tick_value > mCurMaxBar)
+ {
+ break;
+ }
+ }
+ }
+}
diff --git a/indra/llui/llstatbar.h b/indra/llui/llstatbar.h
index 62a9db82fe..1ff4c67fc5 100644..100755
--- a/indra/llui/llstatbar.h
+++ b/indra/llui/llstatbar.h
@@ -29,74 +29,91 @@
#include "llview.h"
#include "llframetimer.h"
-
-class LLStat;
+#include "lltracerecording.h"
class LLStatBar : public LLView
{
public:
+
struct Params : public LLInitParam::Block<Params, LLView::Params>
{
- Optional<std::string> label;
- Optional<std::string> unit_label;
- Optional<F32> bar_min;
- Optional<F32> bar_max;
- Optional<F32> tick_spacing;
- Optional<F32> label_spacing;
- Optional<U32> precision;
- Optional<F32> update_rate;
- Optional<bool> show_per_sec;
- Optional<bool> show_bar;
- Optional<bool> show_history;
- Optional<bool> show_mean;
- Optional<std::string> stat;
- Params()
- : label("label"),
- unit_label("unit_label"),
- bar_min("bar_min", 0.0f),
- bar_max("bar_max", 50.0f),
- tick_spacing("tick_spacing", 10.0f),
- label_spacing("label_spacing", 10.0f),
- precision("precision", 0),
- update_rate("update_rate", 5.0f),
- show_per_sec("show_per_sec", TRUE),
- show_bar("show_bar", TRUE),
- show_history("show_history", FALSE),
- show_mean("show_mean", TRUE),
- stat("stat")
- {
- follows.flags(FOLLOWS_TOP | FOLLOWS_LEFT);
- }
+ Optional<std::string> label,
+ unit_label;
+
+ Optional<F32> bar_min,
+ bar_max,
+ tick_spacing;
+
+ Optional<bool> show_bar,
+ show_history,
+ scale_range;
+
+ Optional<S32> decimal_digits,
+ num_frames,
+ num_frames_short,
+ max_height;
+ Optional<std::string> stat;
+ Optional<EOrientation> orientation;
+
+ Params();
};
LLStatBar(const Params&);
virtual void draw();
virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+
+ void setStat(const std::string& stat_name);
- void setStat(LLStat* stat) { mStatp = stat; }
- void setRange(F32 bar_min, F32 bar_max, F32 tick_spacing, F32 label_spacing);
- void getRange(F32& bar_min, F32& bar_max) { bar_min = mMinBar; bar_max = mMaxBar; }
+ void setRange(F32 bar_min, F32 bar_max);
+ void getRange(F32& bar_min, F32& bar_max) { bar_min = mTargetMinBar; bar_max = mTargetMaxBar; }
/*virtual*/ LLRect getRequiredRect(); // Return the height of this object, given the set options.
private:
- F32 mMinBar;
- F32 mMaxBar;
- F32 mTickSpacing;
- F32 mLabelSpacing;
- U32 mPrecision;
- F32 mUpdatesPerSec;
- BOOL mPerSec; // Use the per sec stats.
- BOOL mDisplayBar; // Display the bar graph.
- BOOL mDisplayHistory;
- BOOL mDisplayMean; // If true, display mean, if false, display current value
-
- LLStat* mStatp;
-
- LLFrameTimer mUpdateTimer;
- LLUIString mLabel;
- std::string mUnitLabel;
- F32 mValue;
+ void drawLabelAndValue( F32 mean, std::string &unit_label, LLRect &bar_rect, S32 decimal_digits );
+ void drawTicks( F32 min, F32 max, F32 value_scale, LLRect &bar_rect );
+
+ F32 mTargetMinBar,
+ mTargetMaxBar,
+ mFloatingTargetMinBar,
+ mFloatingTargetMaxBar,
+ mCurMaxBar,
+ mCurMinBar,
+ mTickSpacing;
+ S32 mDecimalDigits,
+ mNumHistoryFrames,
+ mNumShortHistoryFrames;
+ S32 mMaxHeight;
+ EOrientation mOrientation;
+ F32 mLastDisplayValue;
+ LLFrameTimer mLastDisplayValueTimer;
+
+ enum
+ {
+ STAT_NONE,
+ STAT_COUNT,
+ STAT_EVENT,
+ STAT_SAMPLE,
+ STAT_MEM
+ } mStatType;
+
+ union
+ {
+ void* valid;
+ const LLTrace::StatType<LLTrace::CountAccumulator>* countStatp;
+ const LLTrace::StatType<LLTrace::EventAccumulator>* eventStatp;
+ const LLTrace::StatType<LLTrace::SampleAccumulator>* sampleStatp;
+ const LLTrace::StatType<LLTrace::MemAccumulator>* memStatp;
+ } mStat;
+
+ LLUIString mLabel;
+ std::string mUnitLabel;
+
+ bool mDisplayBar, // Display the bar graph.
+ mDisplayHistory,
+ mAutoScaleMax,
+ mAutoScaleMin;
};
#endif
diff --git a/indra/llui/llstatgraph.cpp b/indra/llui/llstatgraph.cpp
index e44887ebf0..3fe314e77a 100644..100755
--- a/indra/llui/llstatgraph.cpp
+++ b/indra/llui/llstatgraph.cpp
@@ -32,48 +32,52 @@
#include "llmath.h"
#include "llui.h"
-#include "llstat.h"
#include "llgl.h"
#include "llglheaders.h"
+#include "lltracerecording.h"
+#include "lltracethreadrecorder.h"
//#include "llviewercontrol.h"
///////////////////////////////////////////////////////////////////////////////////
-LLStatGraph::LLStatGraph(const LLView::Params& p)
-: LLView(p)
+LLStatGraph::LLStatGraph(const Params& p)
+: LLView(p),
+ mMin(p.min),
+ mMax(p.max),
+ mPerSec(p.per_sec),
+ mPrecision(p.precision),
+ mValue(p.value),
+ mUnits(p.units),
+ mNewStatFloatp(p.stat.count_stat_float)
{
- mStatp = NULL;
setToolTip(p.name());
- mNumThresholds = 3;
- mThresholdColors[0] = LLColor4(0.f, 1.f, 0.f, 1.f);
- mThresholdColors[1] = LLColor4(1.f, 1.f, 0.f, 1.f);
- mThresholdColors[2] = LLColor4(1.f, 0.f, 0.f, 1.f);
- mThresholdColors[3] = LLColor4(1.f, 0.f, 0.f, 1.f);
- mThresholds[0] = 50.f;
- mThresholds[1] = 75.f;
- mThresholds[2] = 100.f;
- mMin = 0.f;
- mMax = 125.f;
- mPerSec = TRUE;
- mValue = 0.f;
- mPrecision = 0;
+
+ for(LLInitParam::ParamIterator<ThresholdParams>::const_iterator it = p.thresholds.threshold.begin(), end_it = p.thresholds.threshold.end();
+ it != end_it;
+ ++it)
+ {
+ mThresholds.push_back(Threshold(it->value(), it->color));
+ }
}
void LLStatGraph::draw()
{
F32 range, frac;
range = mMax - mMin;
- if (mStatp)
+ if (mNewStatFloatp)
{
+ LLTrace::Recording& recording = LLTrace::get_frame_recording().getLastRecording();
+
if (mPerSec)
{
- mValue = mStatp->getMeanPerSec();
+ mValue = recording.getPerSec(*mNewStatFloatp);
}
else
{
- mValue = mStatp->getMean();
+ mValue = recording.getSum(*mNewStatFloatp);
}
}
+
frac = (mValue - mMin) / range;
frac = llmax(0.f, frac);
frac = llmin(1.f, frac);
@@ -91,19 +95,13 @@ void LLStatGraph::draw()
LLColor4 color;
- S32 i;
- for (i = 0; i < mNumThresholds - 1; i++)
+ threshold_vec_t::iterator it = std::lower_bound(mThresholds.begin(), mThresholds.end(), Threshold(mValue / mMax, LLUIColor()));
+
+ if (it != mThresholds.begin())
{
- if (mThresholds[i] > mValue)
- {
- break;
- }
+ it--;
}
- //gl_drop_shadow(0, getRect().getHeight(), getRect().getWidth(), 0,
- // LLUIColorTable::instance().getColor("ColorDropShadow"),
- // (S32) gSavedSettings.getF32("DropShadowFloater") );
-
color = LLUIColorTable::instance().getColor( "MenuDefaultBgColor" );
gGL.color4fv(color.mV);
gl_rect_2d(0, getRect().getHeight(), getRect().getWidth(), 0, TRUE);
@@ -111,14 +109,9 @@ void LLStatGraph::draw()
gGL.color4fv(LLColor4::black.mV);
gl_rect_2d(0, getRect().getHeight(), getRect().getWidth(), 0, FALSE);
- color = mThresholdColors[i];
+ color = it->mColor;
gGL.color4fv(color.mV);
- gl_rect_2d(1, llround(frac*getRect().getHeight()), getRect().getWidth() - 1, 0, TRUE);
-}
-
-void LLStatGraph::setValue(const LLSD& value)
-{
- mValue = (F32)value.asReal();
+ gl_rect_2d(1, ll_round(frac*getRect().getHeight()), getRect().getWidth() - 1, 0, TRUE);
}
void LLStatGraph::setMin(const F32 min)
@@ -131,27 +124,3 @@ void LLStatGraph::setMax(const F32 max)
mMax = max;
}
-void LLStatGraph::setStat(LLStat *statp)
-{
- mStatp = statp;
-}
-
-void LLStatGraph::setLabel(const std::string& label)
-{
- mLabel = label;
-}
-
-void LLStatGraph::setUnits(const std::string& units)
-{
- mUnits = units;
-}
-
-void LLStatGraph::setPrecision(const S32 precision)
-{
- mPrecision = precision;
-}
-
-void LLStatGraph::setThreshold(const S32 i, F32 value)
-{
- mThresholds[i] = value;
-}
diff --git a/indra/llui/llstatgraph.h b/indra/llui/llstatgraph.h
index 757525e232..f381e92a4d 100644..100755
--- a/indra/llui/llstatgraph.h
+++ b/indra/llui/llstatgraph.h
@@ -30,29 +30,84 @@
#include "llview.h"
#include "llframetimer.h"
#include "v4color.h"
-
-class LLStat;
+#include "lltrace.h"
class LLStatGraph : public LLView
{
public:
- LLStatGraph(const LLView::Params&);
+ struct ThresholdParams : public LLInitParam::Block<ThresholdParams>
+ {
+ Mandatory<F32> value;
+ Optional<LLUIColor> color;
- virtual void draw();
+ ThresholdParams()
+ : value("value"),
+ color("color", LLColor4::white)
+ {}
+ };
+
+ struct Thresholds : public LLInitParam::Block<Thresholds>
+ {
+ Multiple<ThresholdParams> threshold;
+
+ Thresholds()
+ : threshold("threshold")
+ {}
+ };
+
+ struct StatParams : public LLInitParam::ChoiceBlock<StatParams>
+ {
+ Alternative<LLTrace::StatType<LLTrace::CountAccumulator>* > count_stat_float;
+ Alternative<LLTrace::StatType<LLTrace::EventAccumulator>* > event_stat_float;
+ Alternative<LLTrace::StatType<LLTrace::SampleAccumulator>* > sample_stat_float;
+ };
+
+ struct Params : public LLInitParam::Block<Params, LLView::Params>
+ {
+ Mandatory<StatParams> stat;
+ Optional<std::string> label,
+ units;
+ Optional<S32> precision;
+ Optional<F32> min,
+ max;
+ Optional<bool> per_sec;
+ Optional<F32> value;
+
+ Optional<Thresholds> thresholds;
+
+ Params()
+ : stat("stat"),
+ label("label"),
+ units("units"),
+ precision("precision", 0),
+ min("min", 0.f),
+ max("max", 125.f),
+ per_sec("per_sec", true),
+ value("value", 0.f),
+ thresholds("thresholds")
+ {
+ Thresholds _thresholds;
+ _thresholds.threshold.add(ThresholdParams().value(0.f).color(LLColor4::green))
+ .add(ThresholdParams().value(0.33f).color(LLColor4::yellow))
+ .add(ThresholdParams().value(0.5f).color(LLColor4::red))
+ .add(ThresholdParams().value(0.75f).color(LLColor4::red));
+ thresholds = _thresholds;
+ }
+ };
+ LLStatGraph(const Params&);
- void setLabel(const std::string& label);
- void setUnits(const std::string& units);
- void setPrecision(const S32 precision);
- void setStat(LLStat *statp);
- void setThreshold(const S32 i, F32 value);
void setMin(const F32 min);
void setMax(const F32 max);
+ virtual void draw();
+
/*virtual*/ void setValue(const LLSD& value);
- LLStat *mStatp;
- BOOL mPerSec;
private:
+ LLTrace::StatType<LLTrace::CountAccumulator>* mNewStatFloatp;
+
+ BOOL mPerSec;
+
F32 mValue;
F32 mMin;
@@ -62,9 +117,25 @@ private:
std::string mUnits;
S32 mPrecision; // Num of digits of precision after dot
- S32 mNumThresholds;
- F32 mThresholds[4];
- LLColor4 mThresholdColors[4];
+ struct Threshold
+ {
+ Threshold(F32 value, const LLUIColor& color)
+ : mValue(value),
+ mColor(color)
+ {}
+
+ F32 mValue;
+ LLUIColor mColor;
+ bool operator <(const Threshold& other)
+ {
+ return mValue < other.mValue;
+ }
+ };
+ typedef std::vector<Threshold> threshold_vec_t;
+ threshold_vec_t mThresholds;
+ //S32 mNumThresholds;
+ //F32 mThresholds[4];
+ //LLColor4 mThresholdColors[4];
};
#endif // LL_LLSTATGRAPH_H
diff --git a/indra/llui/llstatview.cpp b/indra/llui/llstatview.cpp
index eda2d6047f..eda2d6047f 100644..100755
--- a/indra/llui/llstatview.cpp
+++ b/indra/llui/llstatview.cpp
diff --git a/indra/llui/llstatview.h b/indra/llui/llstatview.h
index 22a9fcd672..bc78d3b5fd 100644..100755
--- a/indra/llui/llstatview.h
+++ b/indra/llui/llstatview.h
@@ -46,7 +46,8 @@ public:
Params()
: setting("setting")
{
- follows.flags(FOLLOWS_TOP | FOLLOWS_LEFT);
+ changeDefault(follows.flags, FOLLOWS_TOP | FOLLOWS_LEFT | FOLLOWS_RIGHT);
+ changeDefault(show_label, true);
}
};
diff --git a/indra/llui/llstyle.cpp b/indra/llui/llstyle.cpp
index 28a064e6b6..bb731f4f7e 100644..100755
--- a/indra/llui/llstyle.cpp
+++ b/indra/llui/llstyle.cpp
@@ -88,7 +88,7 @@ void LLStyle::setVisible(BOOL is_visible)
mVisible = is_visible;
}
-LLUIImagePtr LLStyle::getImage() const
+LLPointer<LLUIImage> LLStyle::getImage() const
{
return mImagep;
}
diff --git a/indra/llui/llstyle.h b/indra/llui/llstyle.h
index 322edc343c..9f1eba79d8 100644..100755
--- a/indra/llui/llstyle.h
+++ b/indra/llui/llstyle.h
@@ -30,6 +30,7 @@
#include "v4color.h"
#include "llui.h"
#include "llinitparam.h"
+#include "lluiimage.h"
class LLFontGL;
@@ -72,7 +73,7 @@ public:
void setLinkHREF(const std::string& href);
BOOL isLink() const;
- LLUIImagePtr getImage() const;
+ LLPointer<LLUIImage> getImage() const;
void setImage(const LLUUID& src);
void setImage(const std::string& name);
@@ -108,7 +109,7 @@ private:
const LLFontGL* mFont;
std::string mLink;
bool mIsLink;
- LLUIImagePtr mImagep;
+ LLPointer<LLUIImage> mImagep;
};
typedef LLPointer<LLStyle> LLStyleSP;
diff --git a/indra/llui/lltabcontainer.cpp b/indra/llui/lltabcontainer.cpp
index 7f0d650403..6f858cdeb3 100644..100755
--- a/indra/llui/lltabcontainer.cpp
+++ b/indra/llui/lltabcontainer.cpp
@@ -27,7 +27,7 @@
#include "linden_common.h"
#include "lltabcontainer.h"
-
+#include "llviewereventrecorder.h"
#include "llfocusmgr.h"
#include "lllocalcliprect.h"
#include "llrect.h"
@@ -98,24 +98,25 @@ class LLCustomButtonIconCtrl : public LLButton
{
public:
struct Params
- : public LLInitParam::Block<Params, LLButton::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)
+ Params()
+ : icon_ctrl_pad("icon_ctrl_pad", 1)
{}
};
protected:
friend class LLUICtrlFactory;
- LLCustomButtonIconCtrl(const Params& p):
- LLButton(p),
+
+ LLCustomButtonIconCtrl(const Params& p)
+ : LLButton(p),
mIcon(NULL),
mIconAlignment(LLFontGL::HCENTER),
mIconCtrlPad(p.icon_ctrl_pad)
- {}
+ {}
public:
@@ -214,13 +215,11 @@ LLTabContainer::Params::Params()
middle_tab("middle_tab"),
last_tab("last_tab"),
use_custom_icon_ctrl("use_custom_icon_ctrl", false),
+ open_tabs_on_drag_and_drop("open_tabs_on_drag_and_drop", false),
tab_icon_ctrl_pad("tab_icon_ctrl_pad", 0),
use_ellipses("use_ellipses"),
font_halign("halign")
-{
- name(std::string("tab_container"));
- mouse_opaque = false;
-}
+{}
LLTabContainer::LLTabContainer(const LLTabContainer::Params& p)
: LLPanel(p),
@@ -253,6 +252,7 @@ LLTabContainer::LLTabContainer(const LLTabContainer::Params& p)
mMiddleTabParams(p.middle_tab),
mLastTabParams(p.last_tab),
mCustomIconCtrlUsed(p.use_custom_icon_ctrl),
+ mOpenTabsOnDragAndDrop(p.open_tabs_on_drag_and_drop),
mTabIconCtrlPad(p.tab_icon_ctrl_pad),
mUseTabEllipses(p.use_ellipses)
{
@@ -281,6 +281,7 @@ LLTabContainer::LLTabContainer(const LLTabContainer::Params& p)
LLTabContainer::~LLTabContainer()
{
std::for_each(mTabList.begin(), mTabList.end(), DeletePointer());
+ mTabList.clear();
}
//virtual
@@ -406,7 +407,7 @@ void LLTabContainer::draw()
}
}
- setScrollPosPixels((S32)lerp((F32)getScrollPosPixels(), (F32)target_pixel_scroll, LLCriticalDamp::getInterpolant(0.08f)));
+ setScrollPosPixels((S32)lerp((F32)getScrollPosPixels(), (F32)target_pixel_scroll, LLSmoothInterpolation::getInterpolant(0.08f)));
BOOL has_scroll_arrows = !getTabsHidden() && ((mMaxScrollPos > 0) || (mScrollPosPixels > 0));
if (!mIsVertical)
@@ -506,8 +507,8 @@ void LLTabContainer::draw()
}
}
- mPrevArrowBtn->setFlashing(FALSE);
- mNextArrowBtn->setFlashing(FALSE);
+ mPrevArrowBtn->setFlashing(false);
+ mNextArrowBtn->setFlashing(false);
}
@@ -551,23 +552,23 @@ BOOL LLTabContainer::handleMouseDown( S32 x, S32 y, MASK mask )
}
S32 tab_count = getTabCount();
- if (tab_count > 0)
+ if (tab_count > 0 && !getTabsHidden())
{
LLTabTuple* firsttuple = getTab(0);
LLRect tab_rect;
if (mIsVertical)
{
tab_rect = LLRect(firsttuple->mButton->getRect().mLeft,
- has_scroll_arrows ? mPrevArrowBtn->getRect().mBottom - tabcntrv_pad : mPrevArrowBtn->getRect().mTop,
- firsttuple->mButton->getRect().mRight,
- has_scroll_arrows ? mNextArrowBtn->getRect().mTop + tabcntrv_pad : mNextArrowBtn->getRect().mBottom );
+ has_scroll_arrows ? mPrevArrowBtn->getRect().mBottom - tabcntrv_pad : mPrevArrowBtn->getRect().mTop,
+ firsttuple->mButton->getRect().mRight,
+ has_scroll_arrows ? mNextArrowBtn->getRect().mTop + tabcntrv_pad : mNextArrowBtn->getRect().mBottom );
}
else
{
tab_rect = LLRect(has_scroll_arrows ? mPrevArrowBtn->getRect().mRight : mJumpPrevArrowBtn->getRect().mLeft,
- firsttuple->mButton->getRect().mTop,
- has_scroll_arrows ? mNextArrowBtn->getRect().mLeft : mJumpNextArrowBtn->getRect().mRight,
- firsttuple->mButton->getRect().mBottom );
+ firsttuple->mButton->getRect().mTop,
+ has_scroll_arrows ? mNextArrowBtn->getRect().mLeft : mJumpNextArrowBtn->getRect().mRight,
+ firsttuple->mButton->getRect().mBottom );
}
if( tab_rect.pointInRect( x, y ) )
{
@@ -578,6 +579,11 @@ BOOL LLTabContainer::handleMouseDown( S32 x, S32 y, MASK mask )
tab_button->setFocus(TRUE);
}
}
+ if (handled) {
+ // Note: May need to also capture local coords right here ?
+ LLViewerEventRecorder::instance().update_xui(getPathname( ));
+ }
+
return handled;
}
@@ -629,30 +635,33 @@ BOOL LLTabContainer::handleMouseUp( S32 x, S32 y, MASK mask )
BOOL handled = FALSE;
BOOL has_scroll_arrows = (getMaxScrollPos() > 0) && !getTabsHidden();
+ S32 local_x = x - getRect().mLeft;
+ S32 local_y = y - getRect().mBottom;
+
if (has_scroll_arrows)
{
if (mJumpPrevArrowBtn && mJumpPrevArrowBtn->getRect().pointInRect(x, y))
{
- S32 local_x = x - mJumpPrevArrowBtn->getRect().mLeft;
- S32 local_y = y - mJumpPrevArrowBtn->getRect().mBottom;
+ local_x = x - mJumpPrevArrowBtn->getRect().mLeft;
+ local_y = y - mJumpPrevArrowBtn->getRect().mBottom;
handled = mJumpPrevArrowBtn->handleMouseUp(local_x, local_y, mask);
}
else if (mJumpNextArrowBtn && mJumpNextArrowBtn->getRect().pointInRect(x, y))
{
- S32 local_x = x - mJumpNextArrowBtn->getRect().mLeft;
- S32 local_y = y - mJumpNextArrowBtn->getRect().mBottom;
+ local_x = x - mJumpNextArrowBtn->getRect().mLeft;
+ local_y = y - mJumpNextArrowBtn->getRect().mBottom;
handled = mJumpNextArrowBtn->handleMouseUp(local_x, local_y, mask);
}
else if (mPrevArrowBtn && mPrevArrowBtn->getRect().pointInRect(x, y))
{
- S32 local_x = x - mPrevArrowBtn->getRect().mLeft;
- S32 local_y = y - mPrevArrowBtn->getRect().mBottom;
+ local_x = x - mPrevArrowBtn->getRect().mLeft;
+ local_y = y - mPrevArrowBtn->getRect().mBottom;
handled = mPrevArrowBtn->handleMouseUp(local_x, local_y, mask);
}
else if (mNextArrowBtn && mNextArrowBtn->getRect().pointInRect(x, y))
{
- S32 local_x = x - mNextArrowBtn->getRect().mLeft;
- S32 local_y = y - mNextArrowBtn->getRect().mBottom;
+ local_x = x - mNextArrowBtn->getRect().mLeft;
+ local_y = y - mNextArrowBtn->getRect().mBottom;
handled = mNextArrowBtn->handleMouseUp(local_x, local_y, mask);
}
}
@@ -676,6 +685,10 @@ BOOL LLTabContainer::handleMouseUp( S32 x, S32 y, MASK mask )
}
gFocusMgr.setMouseCapture(NULL);
}
+ if (handled) {
+ // Note: may need to capture local coords here
+ LLViewerEventRecorder::instance().update_xui(getPathname( ));
+ }
return handled;
}
@@ -684,7 +697,7 @@ BOOL LLTabContainer::handleToolTip( S32 x, S32 y, MASK mask)
{
static LLUICachedControl<S32> tabcntrv_pad ("UITabCntrvPad", 0);
BOOL handled = LLPanel::handleToolTip( x, y, mask);
- if (!handled && getTabCount() > 0)
+ if (!handled && getTabCount() > 0 && !getTabsHidden())
{
LLTabTuple* firsttuple = getTab(0);
@@ -815,48 +828,62 @@ BOOL LLTabContainer::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EDrag
{
BOOL has_scroll_arrows = (getMaxScrollPos() > 0);
- if( mDragAndDropDelayTimer.getStarted() && mDragAndDropDelayTimer.getElapsedTimeF32() > SCROLL_DELAY_TIME )
+ if(mOpenTabsOnDragAndDrop && !getTabsHidden())
{
- if (has_scroll_arrows)
+ // In that case, we'll open the hovered tab while dragging and dropping items.
+ // This allows for drilling through tabs.
+ if (mDragAndDropDelayTimer.getStarted())
{
- if (mJumpPrevArrowBtn && mJumpPrevArrowBtn->getRect().pointInRect(x, y))
- {
- S32 local_x = x - mJumpPrevArrowBtn->getRect().mLeft;
- S32 local_y = y - mJumpPrevArrowBtn->getRect().mBottom;
- mJumpPrevArrowBtn->handleHover(local_x, local_y, mask);
- }
- if (mJumpNextArrowBtn && mJumpNextArrowBtn->getRect().pointInRect(x, y))
- {
- S32 local_x = x - mJumpNextArrowBtn->getRect().mLeft;
- S32 local_y = y - mJumpNextArrowBtn->getRect().mBottom;
- mJumpNextArrowBtn->handleHover(local_x, local_y, mask);
- }
- if (mPrevArrowBtn->getRect().pointInRect(x, y))
- {
- S32 local_x = x - mPrevArrowBtn->getRect().mLeft;
- S32 local_y = y - mPrevArrowBtn->getRect().mBottom;
- mPrevArrowBtn->handleHover(local_x, local_y, mask);
- }
- else if (mNextArrowBtn->getRect().pointInRect(x, y))
+ if (mDragAndDropDelayTimer.getElapsedTimeF32() > SCROLL_DELAY_TIME)
{
- S32 local_x = x - mNextArrowBtn->getRect().mLeft;
- S32 local_y = y - mNextArrowBtn->getRect().mBottom;
- mNextArrowBtn->handleHover(local_x, local_y, mask);
- }
- }
+ if (has_scroll_arrows)
+ {
+ if (mJumpPrevArrowBtn && mJumpPrevArrowBtn->getRect().pointInRect(x, y))
+ {
+ S32 local_x = x - mJumpPrevArrowBtn->getRect().mLeft;
+ S32 local_y = y - mJumpPrevArrowBtn->getRect().mBottom;
+ mJumpPrevArrowBtn->handleHover(local_x, local_y, mask);
+ }
+ if (mJumpNextArrowBtn && mJumpNextArrowBtn->getRect().pointInRect(x, y))
+ {
+ S32 local_x = x - mJumpNextArrowBtn->getRect().mLeft;
+ S32 local_y = y - mJumpNextArrowBtn->getRect().mBottom;
+ mJumpNextArrowBtn->handleHover(local_x, local_y, mask);
+ }
+ if (mPrevArrowBtn->getRect().pointInRect(x, y))
+ {
+ S32 local_x = x - mPrevArrowBtn->getRect().mLeft;
+ S32 local_y = y - mPrevArrowBtn->getRect().mBottom;
+ mPrevArrowBtn->handleHover(local_x, local_y, mask);
+ }
+ else if (mNextArrowBtn->getRect().pointInRect(x, y))
+ {
+ S32 local_x = x - mNextArrowBtn->getRect().mLeft;
+ S32 local_y = y - mNextArrowBtn->getRect().mBottom;
+ mNextArrowBtn->handleHover(local_x, local_y, mask);
+ }
+ }
- for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
- {
- LLTabTuple* tuple = *iter;
- tuple->mButton->setVisible( TRUE );
- S32 local_x = x - tuple->mButton->getRect().mLeft;
- S32 local_y = y - tuple->mButton->getRect().mBottom;
- if (tuple->mButton->pointInView(local_x, local_y) && tuple->mButton->getEnabled() && !tuple->mTabPanel->getVisible())
- {
- tuple->mButton->onCommit();
+ for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
+ {
+ LLTabTuple* tuple = *iter;
+ tuple->mButton->setVisible( TRUE );
+ S32 local_x = x - tuple->mButton->getRect().mLeft;
+ S32 local_y = y - tuple->mButton->getRect().mBottom;
+ if (tuple->mButton->pointInView(local_x, local_y) && tuple->mButton->getEnabled() && !tuple->mTabPanel->getVisible())
+ {
+ tuple->mButton->onCommit();
+ }
+ }
+ // Stop the timer whether successful or not. Don't let it run forever.
mDragAndDropDelayTimer.stop();
}
}
+ else
+ {
+ // Start a timer so we don't open tabs as soon as we hover on them
+ mDragAndDropDelayTimer.start();
+ }
}
return LLView::handleDragAndDrop(x, y, mask, drop, type, cargo_data, accept, tooltip);
@@ -1026,85 +1053,50 @@ void LLTabContainer::addTabPanel(const TabPanelParams& panel)
}
else
{
+ LLButton::Params& p = (mCustomIconCtrlUsed ? custom_btn_params : normal_btn_params);
+
+ p.rect(btn_rect);
+ p.font(mFont);
+ p.font_halign = mFontHalign;
+ p.label(trimmed_label);
+ p.click_callback.function(boost::bind(&LLTabContainer::onTabBtn, this, _2, child));
+ if (indent)
+ {
+ p.pad_left(indent);
+ }
+ p.pad_bottom( mLabelPadBottom );
+ p.scale_image(true);
+ p.tab_stop(false);
+ p.label_shadow(false);
+ p.follows.flags = FOLLOWS_LEFT;
+
if (mIsVertical)
{
- 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(mFont);
- p.label(trimmed_label);
- p.image_unselected(mMiddleTabParams.tab_left_image_unselected);
- p.image_selected(mMiddleTabParams.tab_left_image_selected);
- p.scale_image(true);
- p.font_halign = mFontHalign;
- p.pad_bottom( mLabelPadBottom );
- p.tab_stop(false);
- p.label_shadow(false);
- if (indent)
- {
- p.pad_left(indent);
- }
-
-
- if(mCustomIconCtrlUsed)
- {
- btn = LLUICtrlFactory::create<LLCustomButtonIconCtrl>(custom_btn_params);
-
- }
- else
- {
- btn = LLUICtrlFactory::create<LLButton>(p);
- }
+ p.name("vtab_"+std::string(child->getName()));
+ p.image_unselected(mMiddleTabParams.tab_left_image_unselected);
+ p.image_selected(mMiddleTabParams.tab_left_image_selected);
+ p.follows.flags = p.follows.flags() | FOLLOWS_TOP;
}
else
+ {
+ p.name("htab_"+std::string(child->getName()));
+ p.visible(false);
+ p.image_unselected(tab_img);
+ p.image_selected(tab_selected_img);
+ p.follows.flags = p.follows.flags() | (getTabPosition() == TOP ? FOLLOWS_TOP : FOLLOWS_BOTTOM);
+ // Try to squeeze in a bit more text
+ p.pad_left( mLabelPadLeft );
+ p.pad_right(2);
+ }
+
+ // *TODO : It seems wrong not to use p in both cases considering the way p is initialized
+ if (mCustomIconCtrlUsed)
{
- 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(mFont);
- p.label(trimmed_label);
- p.visible(false);
- 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( mLabelPadLeft );
- p.pad_right(2);
- p.pad_bottom( mLabelPadBottom );
- p.font_halign = mFontHalign;
- p.follows.flags = FOLLOWS_LEFT;
- p.follows.flags = FOLLOWS_LEFT;
-
- if (indent)
- {
- p.pad_left(indent);
- }
-
- if( getTabPosition() == TOP )
- {
- p.follows.flags = p.follows.flags() | FOLLOWS_TOP;
- }
- else
- {
- p.follows.flags = p.follows.flags() | FOLLOWS_BOTTOM;
- }
-
- if(mCustomIconCtrlUsed)
- {
- btn = LLUICtrlFactory::create<LLCustomButtonIconCtrl>(custom_btn_params);
- }
- else
- {
- btn = LLUICtrlFactory::create<LLButton>(p);
- }
+ btn = LLUICtrlFactory::create<LLCustomButtonIconCtrl>(custom_btn_params);
+ }
+ else
+ {
+ btn = LLUICtrlFactory::create<LLButton>(p);
}
}
@@ -1148,6 +1140,17 @@ void LLTabContainer::addTabPanel(const TabPanelParams& panel)
addChild( btn, 0 );
}
}
+ else
+ {
+ if (textbox)
+ {
+ LLUICtrl::addChild(textbox, 0);
+ }
+ if (btn)
+ {
+ LLUICtrl::addChild(btn, 0);
+ }
+ }
if (child)
{
@@ -1230,11 +1233,17 @@ void LLTabContainer::removeTabPanel(LLPanel* child)
update_images(mTabList[mTabList.size()-2], mLastTabParams, getTabPosition());
}
- removeChild( tuple->mButton );
+ if (!getTabsHidden())
+ {
+ // We need to remove tab buttons only if the tabs are not hidden.
+ removeChild( tuple->mButton );
+ }
delete tuple->mButton;
+ tuple->mButton = NULL;
removeChild( tuple->mTabPanel );
// delete tuple->mTabPanel;
+ tuple->mTabPanel = NULL;
mTabList.erase( iter );
delete tuple;
@@ -1281,6 +1290,10 @@ void LLTabContainer::enableTabButton(S32 which, BOOL enable)
{
mTabList[which]->mButton->setEnabled(enable);
}
+ // Stop the DaD timer as it might run forever
+ // enableTabButton() is typically called on refresh and draw when anything changed
+ // in the tab container so it's a good time to reset that.
+ mDragAndDropDelayTimer.stop();
}
void LLTabContainer::deleteAllTabs()
@@ -1292,9 +1305,11 @@ void LLTabContainer::deleteAllTabs()
removeChild( tuple->mButton );
delete tuple->mButton;
+ tuple->mButton = NULL;
removeChild( tuple->mTabPanel );
// delete tuple->mTabPanel;
+ tuple->mTabPanel = NULL;
}
// Actually delete the tuples themselves
@@ -1497,13 +1512,20 @@ 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 );
- // RN: this limits tab-stops to active button only, which would require arrow keys to switch tabs
- tuple->mButton->setTabStop( is_selected );
+ // Although the selected tab must be complete, we may have hollow LLTabTuple tucked in the list
+ if (tuple && tuple->mButton)
+ {
+ tuple->mButton->setUseEllipses(mUseTabEllipses);
+ tuple->mButton->setHAlign(mFontHalign);
+ tuple->mButton->setToggleState( is_selected );
+ // RN: this limits tab-stops to active button only, which would require arrow keys to switch tabs
+ tuple->mButton->setTabStop( is_selected );
+ }
+ if (tuple && tuple->mTabPanel)
+ {
+ tuple->mTabPanel->setVisible( is_selected );
+ //tuple->mTabPanel->setFocus(is_selected); // not clear that we want to do this here.
+ }
if (is_selected)
{
@@ -1530,7 +1552,7 @@ BOOL LLTabContainer::setTab(S32 which)
else
{
S32 available_width_with_arrows = getRect().getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + tabcntr_arrow_btn_size + tabcntr_arrow_btn_size + 1);
- S32 running_tab_width = tuple->mButton->getRect().getWidth();
+ S32 running_tab_width = (tuple && tuple->mButton ? tuple->mButton->getRect().getWidth() : 0);
S32 j = i - 1;
S32 min_scroll_pos = i;
if (running_tab_width < available_width_with_arrows)
@@ -1538,7 +1560,7 @@ BOOL LLTabContainer::setTab(S32 which)
while (j >= 0)
{
LLTabTuple* other_tuple = getTab(j);
- running_tab_width += other_tuple->mButton->getRect().getWidth();
+ running_tab_width += (other_tuple && other_tuple->mButton ? other_tuple->mButton->getRect().getWidth() : 0);
if (running_tab_width > available_width_with_arrows)
{
break;
@@ -1574,8 +1596,7 @@ BOOL LLTabContainer::selectTabByName(const std::string& name)
LLPanel* panel = getPanelByName(name);
if (!panel)
{
- llwarns << "LLTabContainer::selectTabByName("
- << name << ") failed" << llendl;
+ LL_WARNS() << "LLTabContainer::selectTabByName(" << name << ") failed" << LL_ENDL;
return FALSE;
}
@@ -1626,16 +1647,26 @@ void LLTabContainer::setTabImage(LLPanel* child, LLIconCtrl* icon)
{
LLTabTuple* tuple = getTabByPanel(child);
LLCustomButtonIconCtrl* button;
+ bool hasButton = false;
if(tuple)
{
button = dynamic_cast<LLCustomButtonIconCtrl*>(tuple->mButton);
if(button)
{
+ hasButton = true;
button->setIcon(icon);
reshapeTuple(tuple);
}
}
+
+ if (!hasButton && (icon != NULL))
+ {
+ // It was assumed that the tab's button would take ownership of the icon pointer.
+ // But since the tab did not have a button, kill the icon to prevent the memory
+ // leak.
+ icon->die();
+ }
}
void LLTabContainer::reshapeTuple(LLTabTuple* tuple)
diff --git a/indra/llui/lltabcontainer.h b/indra/llui/lltabcontainer.h
index eaa2fd54e0..57862fc626 100644..100755
--- a/indra/llui/lltabcontainer.h
+++ b/indra/llui/lltabcontainer.h
@@ -105,6 +105,11 @@ public:
Optional<bool> use_custom_icon_ctrl;
/**
+ * Open tabs on hover in drag and drop situations
+ */
+ Optional<bool> open_tabs_on_drag_and_drop;
+
+ /**
* Paddings for LLIconCtrl in case of LLCustomButtonIconCtrl usage(use_custom_icon_ctrl = true)
*/
Optional<S32> tab_icon_ctrl_pad;
@@ -183,10 +188,11 @@ public:
void selectFirstTab();
void selectLastTab();
void selectNextTab();
- void selectPrevTab();
+ void selectPrevTab();
BOOL selectTabPanel( LLPanel* child );
BOOL selectTab(S32 which);
BOOL selectTabByName(const std::string& title);
+ void setCurrentPanelIndex(S32 index) { mCurrentTabIdx = index; }
BOOL getTabPanelFlashing(LLPanel* child);
void setTabPanelFlashing(LLPanel* child, BOOL state);
@@ -237,8 +243,6 @@ private:
void setTabsHidden(BOOL hidden) { mTabsHidden = hidden; }
BOOL getTabsHidden() const { return mTabsHidden; }
-
- void setCurrentPanelIndex(S32 index) { mCurrentTabIdx = index; }
void scrollPrev() { mScrollPos = llmax(0, mScrollPos-1); } // No wrap
void scrollNext() { mScrollPos = llmin(mScrollPos+1, mMaxScrollPos); } // No wrap
@@ -300,6 +304,7 @@ private:
TabParams mLastTabParams;
bool mCustomIconCtrlUsed;
+ bool mOpenTabsOnDragAndDrop;
S32 mTabIconCtrlPad;
bool mUseTabEllipses;
};
diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp
index 49537ef78f..602a703450 100644..100755
--- a/indra/llui/lltextbase.cpp
+++ b/indra/llui/lltextbase.cpp
@@ -32,6 +32,7 @@
#include "lllocalcliprect.h"
#include "llmenugl.h"
#include "llscrollcontainer.h"
+#include "llspellcheck.h"
#include "llstl.h"
#include "lltextparser.h"
#include "lltextutil.h"
@@ -45,6 +46,7 @@
const F32 CURSOR_FLASH_DELAY = 1.0f; // in seconds
const S32 CURSOR_THICKNESS = 2;
+const F32 TRIPLE_CLICK_INTERVAL = 0.3f; // delay between double and triple click.
LLTextBase::line_info::line_info(S32 index_start, S32 index_end, LLRect rect, S32 line_num)
: mDocIndexStart(index_start),
@@ -68,43 +70,36 @@ bool LLTextBase::compare_segment_end::operator()(const LLTextSegmentPtr& a, cons
// helper functors
-struct LLTextBase::compare_bottom
+bool LLTextBase::compare_bottom::operator()(const S32& a, const LLTextBase::line_info& b) const
{
- 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
- }
+ return a > b.mRect.mBottom; // 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
- }
+bool LLTextBase::compare_bottom::operator()(const LLTextBase::line_info& a, const S32& b) const
+{
+ return a.mRect.mBottom > b; // bottom of a is higher than bottom of b
+}
-};
+bool LLTextBase::compare_bottom::operator()(const LLTextBase::line_info& a, const LLTextBase::line_info& b) const
+{
+ return a.mRect.mBottom > b.mRect.mBottom; // bottom of a is higher than bottom of b
+}
// helper functors
-struct LLTextBase::compare_top
+bool LLTextBase::compare_top::operator()(const S32& a, const LLTextBase::line_info& b) const
{
- 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
- }
+ 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 LLTextBase::compare_top::operator()(const LLTextBase::line_info& a, const S32& b) const
+{
+ return a.mRect.mTop > b; // top of a is higher than top of b
+}
- bool 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
- }
-};
+bool LLTextBase::compare_top::operator()(const LLTextBase::line_info& a, const LLTextBase::line_info& b) const
+{
+ return a.mRect.mTop > b.mRect.mTop; // top of a is higher than top of b
+}
struct LLTextBase::line_end_compare
{
@@ -144,6 +139,7 @@ LLTextBase::Params::Params()
: cursor_color("cursor_color"),
text_color("text_color"),
text_readonly_color("text_readonly_color"),
+ text_tentative_color("text_tentative_color"),
bg_visible("bg_visible", false),
border_visible("border_visible", false),
bg_readonly_color("bg_readonly_color"),
@@ -155,13 +151,16 @@ LLTextBase::Params::Params()
plain_text("plain_text",false),
track_end("track_end", false),
read_only("read_only", false),
+ spellcheck("spellcheck", false),
v_pad("v_pad", 0),
h_pad("h_pad", 0),
+ clip("clip", true),
clip_partial("clip_partial", true),
line_spacing("line_spacing"),
max_text_length("max_length", 255),
font_shadow("font_shadow"),
wrap("wrap"),
+ trusted_content("trusted_content", true),
use_ellipses("use_ellipses", false),
parse_urls("parse_urls", false),
parse_highlights("parse_highlights", false)
@@ -175,15 +174,20 @@ LLTextBase::Params::Params()
LLTextBase::LLTextBase(const LLTextBase::Params &p)
: LLUICtrl(p, LLTextViewModelPtr(new LLTextViewModel)),
mURLClickSignal(NULL),
+ mIsFriendSignal(NULL),
mMaxTextByteLength( p.max_text_length ),
- mDefaultFont(p.font),
+ mFont(p.font),
mFontShadow(p.font_shadow),
mPopupMenu(NULL),
mReadOnly(p.read_only),
+ mSpellCheck(p.spellcheck),
+ mSpellCheckStart(-1),
+ mSpellCheckEnd(-1),
mCursorColor(p.cursor_color),
mFgColor(p.text_color),
mBorderVisible( p.border_visible ),
mReadOnlyFgColor(p.text_readonly_color),
+ mTentativeFgColor(p.text_tentative_color()),
mWriteableBgColor(p.bg_writeable_color),
mReadOnlyBgColor(p.bg_readonly_color),
mFocusBgColor(p.bg_focus_color),
@@ -199,7 +203,9 @@ LLTextBase::LLTextBase(const LLTextBase::Params &p)
mVAlign(p.font_valign),
mLineSpacingMult(p.line_spacing.multiple),
mLineSpacingPixels(p.line_spacing.pixels),
+ mClip(p.clip),
mClipPartial(p.clip_partial && !p.allow_scroll),
+ mTrustedContent(p.trusted_content),
mTrackEnd( p.track_end ),
mScrollIndex(-1),
mSelectionStart( 0 ),
@@ -244,6 +250,12 @@ LLTextBase::LLTextBase(const LLTextBase::Params &p)
addChild(mDocumentView);
}
+ if (mSpellCheck)
+ {
+ LLSpellChecker::setSettingsChangeCallback(boost::bind(&LLTextBase::onSpellCheckSettingsChange, this));
+ }
+ mSpellCheckTimer.reset();
+
createDefaultSegment();
updateRects();
@@ -278,12 +290,23 @@ bool LLTextBase::truncate()
if (getLength() >= S32(mMaxTextByteLength / 4))
{
// Have to check actual byte size
- LLWString text(getWText());
- S32 utf8_byte_size = wstring_utf8_length(text);
+ S32 utf8_byte_size = 0;
+ LLSD value = getViewModel()->getValue();
+ if (value.type() == LLSD::TypeString)
+ {
+ // save a copy for strings.
+ utf8_byte_size = value.size();
+ }
+ else
+ {
+ // non string LLSDs need explicit conversion to string
+ utf8_byte_size = value.asString().size();
+ }
+
if ( utf8_byte_size > mMaxTextByteLength )
{
// Truncate safely in UTF-8
- std::string temp_utf8_text = wstring_to_utf8str(text);
+ std::string temp_utf8_text = value.asString();
temp_utf8_text = utf8str_truncate( temp_utf8_text, mMaxTextByteLength );
LLWString text = utf8str_to_wstring( temp_utf8_text );
// remove extra bit of current string, to preserve formatting, etc.
@@ -295,21 +318,26 @@ bool LLTextBase::truncate()
return did_truncate;
}
-const LLStyle::Params& LLTextBase::getDefaultStyleParams()
+const LLStyle::Params& LLTextBase::getStyleParams()
{
//FIXME: convert mDefaultStyle to a flyweight http://www.boost.org/doc/libs/1_40_0/libs/flyweight/doc/index.html
//and eliminate color member values
if (mStyleDirty)
{
- mDefaultStyle
+ mStyle
.color(LLUIColor(&mFgColor)) // pass linked color instead of copy of mFGColor
.readonly_color(LLUIColor(&mReadOnlyFgColor))
.selected_color(LLUIColor(&mTextSelectedColor))
- .font(mDefaultFont)
+ .font(mFont)
.drop_shadow(mFontShadow);
mStyleDirty = false;
}
- return mDefaultStyle;
+ return mStyle;
+}
+
+void LLTextBase::beforeValueChange()
+{
+
}
void LLTextBase::onValueChange(S32 start, S32 end)
@@ -327,14 +355,13 @@ void LLTextBase::drawSelectionBackground()
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());
+ line_list_t::const_iterator end_iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), content_display_rect.mBottom, compare_top());
bool done = false;
@@ -412,6 +439,7 @@ void LLTextBase::drawSelectionBackground()
++rect_it)
{
LLRect selection_rect = *rect_it;
+ 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);
}
@@ -489,8 +517,8 @@ void LLTextBase::drawCursor()
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]);
+ ime_pos.mX = (S32) (ime_pos.mX * LLUI::getScaleFactor().mV[VX]);
+ ime_pos.mY = (S32) (ime_pos.mY * LLUI::getScaleFactor().mV[VY]);
getWindow()->setLanguageTextInput( ime_pos );
}
}
@@ -498,11 +526,17 @@ void LLTextBase::drawCursor()
void LLTextBase::drawText()
{
- const S32 text_len = getLength();
- if( text_len <= 0 )
+ S32 text_len = getLength();
+
+ if (text_len <= 0 && mLabel.empty())
{
return;
}
+ else if (useLabel())
+ {
+ text_len = mLabel.getWString().length();
+ }
+
S32 selection_left = -1;
S32 selection_right = -1;
// Draw selection even if we don't have keyboard focus for search/replace
@@ -512,7 +546,6 @@ void LLTextBase::drawText()
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;
@@ -529,8 +562,102 @@ void LLTextBase::drawText()
return;
}
+ // Perform spell check if needed
+ if ( (getSpellCheck()) && (getWText().length() > 2) )
+ {
+ // Calculate start and end indices for the spell checking range
+ S32 start = line_start;
+ S32 end = getLineEnd(last_line);
+
+ if ( (mSpellCheckStart != start) || (mSpellCheckEnd != end) )
+ {
+ const LLWString& wstrText = getWText();
+ mMisspellRanges.clear();
+
+ segment_set_t::const_iterator seg_it = getSegIterContaining(start);
+ while (mSegments.end() != seg_it)
+ {
+ LLTextSegmentPtr text_segment = *seg_it;
+ if ( (text_segment.isNull()) || (text_segment->getStart() >= end) )
+ {
+ break;
+ }
+
+ if (!text_segment->canEdit())
+ {
+ ++seg_it;
+ continue;
+ }
+
+ // Combine adjoining text segments into one
+ U32 seg_start = text_segment->getStart(), seg_end = llmin(text_segment->getEnd(), end);
+ while (mSegments.end() != ++seg_it)
+ {
+ text_segment = *seg_it;
+ if ( (text_segment.isNull()) || (!text_segment->canEdit()) || (text_segment->getStart() >= end) )
+ {
+ break;
+ }
+ seg_end = llmin(text_segment->getEnd(), end);
+ }
+
+ // Find the start of the first word
+ U32 word_start = seg_start, word_end = -1;
+ U32 text_length = wstrText.length();
+ while ( (word_start < text_length) && (!LLStringOps::isAlpha(wstrText[word_start])) )
+ {
+ word_start++;
+ }
+
+ // Iterate over all words in the text block and check them one by one
+ while (word_start < seg_end)
+ {
+ // Find the end of the current word (special case handling for "'" when it's used as a contraction)
+ word_end = word_start + 1;
+ while ( (word_end < seg_end) &&
+ ((LLWStringUtil::isPartOfWord(wstrText[word_end])) ||
+ ((L'\'' == wstrText[word_end]) &&
+ (LLStringOps::isAlnum(wstrText[word_end - 1])) && (LLStringOps::isAlnum(wstrText[word_end + 1])))) )
+ {
+ word_end++;
+ }
+ if (word_end > seg_end)
+ {
+ break;
+ }
+
+ if (word_start < text_length && word_end <= text_length && word_end > word_start)
+ {
+ std::string word = wstring_to_utf8str(wstrText.substr(word_start, word_end - word_start));
+
+ // Don't process words shorter than 3 characters
+ if ( (word.length() >= 3) && (!LLSpellChecker::instance().checkSpelling(word)) )
+ {
+ mMisspellRanges.push_back(std::pair<U32, U32>(word_start, word_end));
+ }
+ }
+
+ // Find the start of the next word
+ word_start = word_end + 1;
+ while ( (word_start < seg_end) && (!LLWStringUtil::isPartOfWord(wstrText[word_start])) )
+ {
+ word_start++;
+ }
+ }
+ }
+
+ mSpellCheckStart = start;
+ mSpellCheckEnd = end;
+ }
+ }
+ else
+ {
+ mMisspellRanges.clear();
+ }
+
LLTextSegmentPtr cur_segment = *seg_iter;
+ std::list<std::pair<U32, U32> >::const_iterator misspell_it = std::lower_bound(mMisspellRanges.begin(), mMisspellRanges.end(), std::pair<U32, U32>(line_start, 0));
for (S32 cur_line = first_line; cur_line < last_line; cur_line++)
{
S32 next_line = cur_line + 1;
@@ -545,10 +672,9 @@ void LLTextBase::drawText()
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);
+ LLRect text_rect(line.mRect);
+ text_rect.mRight = mDocumentView->getRect().getWidth(); // clamp right edge to document extents
+ text_rect.translate(mDocumentView->getRect().mLeft, mDocumentView->getRect().mBottom); // adjust by scroll position
// draw a single line of text
S32 seg_start = line_start;
@@ -559,14 +685,15 @@ void LLTextBase::drawText()
seg_iter++;
if (seg_iter == mSegments.end())
{
- llwarns << "Ran off the segmentation end!" << llendl;
+ LL_WARNS() << "Ran off the segmentation end!" << LL_ENDL;
return;
}
cur_segment = *seg_iter;
}
- S32 clipped_end = llmin( line_end, cur_segment->getEnd() ) - cur_segment->getStart();
+ S32 seg_end = llmin(line_end, cur_segment->getEnd());
+ S32 clipped_end = seg_end - cur_segment->getStart();
if (mUseEllipses // using ellipses
&& clipped_end == line_end // last segment on line
@@ -578,6 +705,46 @@ void LLTextBase::drawText()
text_rect.mRight -= 2;
}
+ // Draw squiggly lines under any visible misspelled words
+ while ( (mMisspellRanges.end() != misspell_it) && (misspell_it->first < seg_end) && (misspell_it->second > seg_start) )
+ {
+ // Skip the current word if the user is still busy editing it
+ if ( (!mSpellCheckTimer.hasExpired()) && (misspell_it->first <= (U32)mCursorPos) && (misspell_it->second >= (U32)mCursorPos) )
+ {
+ ++misspell_it;
+ continue;
+ }
+
+ U32 misspell_start = llmax<U32>(misspell_it->first, seg_start), misspell_end = llmin<U32>(misspell_it->second, seg_end);
+ S32 squiggle_start = 0, squiggle_end = 0, pony = 0;
+ cur_segment->getDimensions(seg_start - cur_segment->getStart(), misspell_start - seg_start, squiggle_start, pony);
+ cur_segment->getDimensions(misspell_start - cur_segment->getStart(), misspell_end - misspell_start, squiggle_end, pony);
+ squiggle_start += text_rect.mLeft;
+
+ pony = (squiggle_end + 3) / 6;
+ squiggle_start += squiggle_end / 2 - pony * 3;
+ squiggle_end = squiggle_start + pony * 6;
+
+ S32 squiggle_bottom = text_rect.mBottom + (S32)cur_segment->getStyle()->getFont()->getDescenderHeight();
+
+ gGL.color4ub(255, 0, 0, 200);
+ while (squiggle_start + 1 < squiggle_end)
+ {
+ gl_line_2d(squiggle_start, squiggle_bottom, squiggle_start + 2, squiggle_bottom - 2);
+ if (squiggle_start + 3 < squiggle_end)
+ {
+ gl_line_2d(squiggle_start + 2, squiggle_bottom - 3, squiggle_start + 4, squiggle_bottom - 1);
+ }
+ squiggle_start += 4;
+ }
+
+ if (misspell_it->second > seg_end)
+ {
+ break;
+ }
+ ++misspell_it;
+ }
+
text_rect.mLeft = (S32)(cur_segment->draw(seg_start - cur_segment->getStart(), clipped_end, selection_left, selection_right, text_rect));
seg_start = clipped_end + cur_segment->getStart();
@@ -592,13 +759,14 @@ void LLTextBase::drawText()
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
+ beforeValueChange();
+
+ S32 old_len = getLength(); // length() returns character length
S32 insert_len = wstr.length();
pos = getEditableIndex(pos, true);
- segment_set_t::iterator seg_iter = getSegIterContaining(pos);
+ segment_set_t::iterator seg_iter = getEditableSegIterContaining(pos);
LLTextSegmentPtr default_segment;
@@ -624,7 +792,7 @@ S32 LLTextBase::insertStringNoUndo(S32 pos, const LLWString &wstr, LLTextBase::s
else
{
// create default editable segment to hold new text
- LLStyleConstSP sp(new LLStyle(getDefaultStyleParams()));
+ LLStyleConstSP sp(new LLStyle(getStyleParams()));
default_segment = new LLNormalTextSegment( sp, pos, pos + insert_len, *this);
}
@@ -653,8 +821,7 @@ S32 LLTextBase::insertStringNoUndo(S32 pos, const LLWString &wstr, LLTextBase::s
}
}
- text.insert(pos, wstr);
- getViewModel()->setDisplay(text);
+ getViewModel()->getEditableDisplay().insert(pos, wstr);
if ( truncate() )
{
@@ -669,7 +836,8 @@ S32 LLTextBase::insertStringNoUndo(S32 pos, const LLWString &wstr, LLTextBase::s
S32 LLTextBase::removeStringNoUndo(S32 pos, S32 length)
{
- LLWString text(getWText());
+
+ beforeValueChange();
segment_set_t::iterator seg_iter = getSegIterContaining(pos);
while(seg_iter != mSegments.end())
{
@@ -715,8 +883,7 @@ S32 LLTextBase::removeStringNoUndo(S32 pos, S32 length)
++seg_iter;
}
- text.erase(pos, length);
- getViewModel()->setDisplay(text);
+ getViewModel()->getEditableDisplay().erase(pos, length);
// recreate default segment in case we erased everything
createDefaultSegment();
@@ -729,13 +896,13 @@ S32 LLTextBase::removeStringNoUndo(S32 pos, S32 length)
S32 LLTextBase::overwriteCharNoUndo(S32 pos, llwchar wc)
{
+ beforeValueChange();
+
if (pos > (S32)getLength())
{
return 0;
}
- LLWString text(getWText());
- text[pos] = wc;
- getViewModel()->setDisplay(text);
+ getViewModel()->getEditableDisplay()[pos] = wc;
onValueChange(pos, pos + 1);
needsReflow(pos);
@@ -749,7 +916,7 @@ void LLTextBase::createDefaultSegment()
// ensures that there is always at least one segment
if (mSegments.empty())
{
- LLStyleConstSP sp(new LLStyle(getDefaultStyleParams()));
+ LLStyleConstSP sp(new LLStyle(getStyleParams()));
LLTextSegmentPtr default_segment = new LLNormalTextSegment( sp, 0, getLength() + 1, *this);
mSegments.insert(default_segment);
default_segment->linkToDocument(this);
@@ -839,6 +1006,13 @@ void LLTextBase::insertSegment(LLTextSegmentPtr segment_to_insert)
BOOL LLTextBase::handleMouseDown(S32 x, S32 y, MASK mask)
{
+ // handle triple click
+ if (!mTripleClickTimer.hasExpired())
+ {
+ selectAll();
+ return TRUE;
+ }
+
LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y);
if (cur_segment && cur_segment->handleMouseDown(x, y, mask))
{
@@ -851,12 +1025,12 @@ BOOL LLTextBase::handleMouseDown(S32 x, S32 y, MASK 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))
+ if (hasMouseCapture() && cur_segment && cur_segment->handleMouseUp(x, y, mask))
{
// Did we just click on a link?
if (mURLClickSignal
&& cur_segment->getStyle()
- && cur_segment->getStyle()->isLink())
+ && cur_segment->getStyle()->isLink())
{
// *TODO: send URL here?
(*mURLClickSignal)(this, LLSD() );
@@ -913,6 +1087,14 @@ BOOL LLTextBase::handleRightMouseUp(S32 x, S32 y, MASK mask)
BOOL LLTextBase::handleDoubleClick(S32 x, S32 y, MASK mask)
{
+ //Don't start triple click timer if user have clicked on scrollbar
+ mVisibleTextRect = mScroller ? mScroller->getContentWindowRect() : getLocalRect();
+ if (x >= mVisibleTextRect.mLeft && x <= mVisibleTextRect.mRight
+ && y >= mVisibleTextRect.mBottom && y <= mVisibleTextRect.mTop)
+ {
+ mTripleClickTimer.setTimerExpirySec(TRIPLE_CLICK_INTERVAL);
+ }
+
LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y);
if (cur_segment && cur_segment->handleDoubleClick(x, y, mask))
{
@@ -993,14 +1175,28 @@ void LLTextBase::draw()
updateScrollFromCursor();
}
- LLRect doc_rect;
+ LLRect text_rect;
if (mScroller)
{
- mScroller->localRectToOtherView(mScroller->getContentWindowRect(), &doc_rect, this);
+ mScroller->localRectToOtherView(mScroller->getContentWindowRect(), &text_rect, this);
}
else
{
- doc_rect = getLocalRect();
+ LLRect visible_lines_rect;
+ std::pair<S32, S32> line_range = getVisibleLines(mClipPartial);
+ for (S32 i = line_range.first; i < line_range.second; i++)
+ {
+ if (visible_lines_rect.isEmpty())
+ {
+ visible_lines_rect = mLineInfoList[i].mRect;
+ }
+ else
+ {
+ visible_lines_rect.unionWith(mLineInfoList[i].mRect);
+ }
+ }
+ text_rect = visible_lines_rect;
+ text_rect.translate(mDocumentView->getRect().mLeft, mDocumentView->getRect().mBottom);
}
if (mBGVisible)
@@ -1010,28 +1206,37 @@ void LLTextBase::draw()
LLRect bg_rect = mVisibleTextRect;
if (mScroller)
{
- bg_rect.intersectWith(doc_rect);
+ bg_rect.intersectWith(text_rect);
}
LLColor4 bg_color = mReadOnly
? mReadOnlyBgColor.get()
: hasFocus()
? mFocusBgColor.get()
: mWriteableBgColor.get();
- gl_rect_2d(doc_rect, bg_color % alpha, TRUE);
+ gl_rect_2d(text_rect, bg_color % alpha, 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);
+ bool should_clip = mClip || mScroller != NULL;
+ { LLLocalClipRect clip(text_rect, should_clip);
+
+ // draw document view
+ if (mScroller)
+ {
+ drawChild(mScroller);
+ }
+ else
+ {
+ drawChild(mDocumentView);
+ }
+
drawSelectionBackground();
drawText();
drawCursor();
}
+
+ mDocumentView->setVisible(FALSE);
+ LLUICtrl::draw();
+ mDocumentView->setVisible(TRUE);
}
@@ -1050,13 +1255,13 @@ void LLTextBase::setReadOnlyColor(const LLColor4 &c)
}
//virtual
-void LLTextBase::handleVisibilityChange( BOOL new_visibility )
+void LLTextBase::onVisibilityChange( BOOL new_visibility )
{
if(!new_visibility && mPopupMenu)
{
mPopupMenu->hide();
}
- LLUICtrl::handleVisibilityChange(new_visibility);
+ LLUICtrl::onVisibilityChange(new_visibility);
}
//virtual
@@ -1080,6 +1285,119 @@ void LLTextBase::deselect()
mIsSelecting = FALSE;
}
+bool LLTextBase::getSpellCheck() const
+{
+ return (LLSpellChecker::getUseSpellCheck()) && (!mReadOnly) && (mSpellCheck);
+}
+
+const std::string& LLTextBase::getSuggestion(U32 index) const
+{
+ return (index < mSuggestionList.size()) ? mSuggestionList[index] : LLStringUtil::null;
+}
+
+U32 LLTextBase::getSuggestionCount() const
+{
+ return mSuggestionList.size();
+}
+
+void LLTextBase::replaceWithSuggestion(U32 index)
+{
+ for (std::list<std::pair<U32, U32> >::const_iterator it = mMisspellRanges.begin(); it != mMisspellRanges.end(); ++it)
+ {
+ if ( (it->first <= (U32)mCursorPos) && (it->second >= (U32)mCursorPos) )
+ {
+ deselect();
+ // Insert the suggestion in its place
+ LLWString suggestion = utf8str_to_wstring(mSuggestionList[index]);
+ insertStringNoUndo(it->first, utf8str_to_wstring(mSuggestionList[index]));
+
+ // Delete the misspelled word
+ removeStringNoUndo(it->first + (S32)suggestion.length(), it->second - it->first);
+
+
+ setCursorPos(it->first + (S32)suggestion.length());
+
+ break;
+ }
+ }
+ mSpellCheckStart = mSpellCheckEnd = -1;
+}
+
+void LLTextBase::addToDictionary()
+{
+ if (canAddToDictionary())
+ {
+ LLSpellChecker::instance().addToCustomDictionary(getMisspelledWord(mCursorPos));
+ }
+}
+
+bool LLTextBase::canAddToDictionary() const
+{
+ return (getSpellCheck()) && (isMisspelledWord(mCursorPos));
+}
+
+void LLTextBase::addToIgnore()
+{
+ if (canAddToIgnore())
+ {
+ LLSpellChecker::instance().addToIgnoreList(getMisspelledWord(mCursorPos));
+ }
+}
+
+bool LLTextBase::canAddToIgnore() const
+{
+ return (getSpellCheck()) && (isMisspelledWord(mCursorPos));
+}
+
+std::string LLTextBase::getMisspelledWord(U32 pos) const
+{
+ for (std::list<std::pair<U32, U32> >::const_iterator it = mMisspellRanges.begin(); it != mMisspellRanges.end(); ++it)
+ {
+ if ( (it->first <= pos) && (it->second >= pos) )
+ {
+ return wstring_to_utf8str(getWText().substr(it->first, it->second - it->first));
+ }
+ }
+ return LLStringUtil::null;
+}
+
+bool LLTextBase::isMisspelledWord(U32 pos) const
+{
+ for (std::list<std::pair<U32, U32> >::const_iterator it = mMisspellRanges.begin(); it != mMisspellRanges.end(); ++it)
+ {
+ if ( (it->first <= pos) && (it->second >= pos) )
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+void LLTextBase::onSpellCheckSettingsChange()
+{
+ // Recheck the spelling on every change
+ mMisspellRanges.clear();
+ mSpellCheckStart = mSpellCheckEnd = -1;
+}
+
+void LLTextBase::onFocusReceived()
+{
+ LLUICtrl::onFocusReceived();
+ if (!getLength() && !mLabel.empty())
+ {
+ // delete label which is LLLabelTextSegment
+ clearSegments();
+ }
+}
+
+void LLTextBase::onFocusLost()
+{
+ LLUICtrl::onFocusLost();
+ if (!getLength() && !mLabel.empty())
+ {
+ resetLabel();
+ }
+}
// Sets the scrollbar from the cursor position
void LLTextBase::updateScrollFromCursor()
@@ -1095,8 +1413,7 @@ void LLTextBase::updateScrollFromCursor()
// 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);
+ LLRect cursor_rect_doc = getDocRectFromDocIndex(mCursorPos);
mScroller->scrollToShowRect(cursor_rect_doc, LLRect(0, scroller_doc_window.getHeight() - 5, scroller_doc_window.getWidth(), 5));
}
@@ -1116,10 +1433,10 @@ S32 LLTextBase::getLeftOffset(S32 width)
}
-static LLFastTimer::DeclareTimer FTM_TEXT_REFLOW ("Text Reflow");
+static LLTrace::BlockTimerStatHandle FTM_TEXT_REFLOW ("Text Reflow");
void LLTextBase::reflow()
{
- LLFastTimer ft(FTM_TEXT_REFLOW);
+ LL_RECORD_BLOCK_TIME(FTM_TEXT_REFLOW);
updateSegments();
@@ -1140,7 +1457,7 @@ void LLTextBase::reflow()
S32 first_line = getFirstVisibleLine();
// if scroll anchor not on first line, update it to first character of first line
- if (!mLineInfoList.empty()
+ if ((first_line < mLineInfoList.size())
&& (mScrollIndex < mLineInfoList[first_line].mDocIndexStart
|| mScrollIndex >= mLineInfoList[first_line].mDocIndexEnd))
{
@@ -1161,7 +1478,7 @@ void LLTextBase::reflow()
// 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;
+ LL_DEBUGS() << "Breaking out of reflow due to possible infinite loop in " << getName() << LL_ENDL;
break;
}
@@ -1170,7 +1487,10 @@ void LLTextBase::reflow()
// 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());
+ if (mWordWrap)
+ {
+ mDocumentView->reshape(mVisibleTextRect.getWidth(), mDocumentView->getRect().getHeight());
+ }
S32 cur_top = 0;
@@ -1236,7 +1556,7 @@ void LLTextBase::reflow()
line_count));
line_start_index = segment->getStart() + seg_offset;
- cur_top -= llround((F32)line_height * mLineSpacingMult) + mLineSpacingPixels;
+ cur_top -= ll_round((F32)line_height * mLineSpacingMult) + mLineSpacingPixels;
remaining_pixels = text_available_width;
line_height = 0;
}
@@ -1248,7 +1568,7 @@ void LLTextBase::reflow()
last_segment_char_on_line,
line_rect,
line_count));
- cur_top -= llround((F32)line_height * mLineSpacingMult) + mLineSpacingPixels;
+ cur_top -= ll_round((F32)line_height * mLineSpacingMult) + mLineSpacingPixels;
break;
}
// ...or finished a segment and there are segments remaining on this line
@@ -1263,7 +1583,7 @@ void LLTextBase::reflow()
line_rect,
line_count));
line_start_index = segment->getStart() + seg_offset;
- cur_top -= llround((F32)line_height * mLineSpacingMult) + mLineSpacingPixels;
+ cur_top -= ll_round((F32)line_height * mLineSpacingMult) + mLineSpacingPixels;
line_height = 0;
remaining_pixels = text_available_width;
}
@@ -1342,9 +1662,9 @@ 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;
@@ -1354,9 +1674,9 @@ 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;
@@ -1415,7 +1735,7 @@ S32 LLTextBase::getFirstVisibleLine() const
return iter - mLineInfoList.begin();
}
-std::pair<S32, S32> LLTextBase::getVisibleLines(bool fully_visible)
+std::pair<S32, S32> LLTextBase::getVisibleLines(bool require_fully_visible)
{
LLRect visible_region = getVisibleDocumentRect();
line_list_t::const_iterator first_iter;
@@ -1424,14 +1744,14 @@ std::pair<S32, S32> LLTextBase::getVisibleLines(bool fully_visible)
// make sure we have an up-to-date mLineInfoList
reflow();
- if (fully_visible)
+ if (require_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());
+ last_iter = std::upper_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());
+ first_iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), visible_region.mTop, compare_bottom());
last_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), visible_region.mBottom, compare_top());
}
return std::pair<S32, S32>(first_iter - mLineInfoList.begin(), last_iter - mLineInfoList.begin());
@@ -1455,10 +1775,10 @@ void LLTextBase::removeDocumentChild(LLView* view)
}
-static LLFastTimer::DeclareTimer FTM_UPDATE_TEXT_SEGMENTS("Update Text Segments");
+static LLTrace::BlockTimerStatHandle FTM_UPDATE_TEXT_SEGMENTS("Update Text Segments");
void LLTextBase::updateSegments()
{
- LLFastTimer ft(FTM_UPDATE_TEXT_SEGMENTS);
+ LL_RECORD_BLOCK_TIME(FTM_UPDATE_TEXT_SEGMENTS);
createDefaultSegment();
}
@@ -1488,16 +1808,65 @@ void LLTextBase::getSegmentAndOffset( S32 startpos, segment_set_t::iterator* seg
}
}
+LLTextBase::segment_set_t::iterator LLTextBase::getEditableSegIterContaining(S32 index)
+{
+ segment_set_t::iterator it = getSegIterContaining(index);
+ segment_set_t::iterator orig_it = it;
+
+ if (it == mSegments.end()) return it;
+
+ if (!(*it)->canEdit()
+ && index == (*it)->getStart()
+ && it != mSegments.begin())
+ {
+ it--;
+ if ((*it)->canEdit())
+ {
+ return it;
+ }
+ }
+ return orig_it;
+}
+
+LLTextBase::segment_set_t::const_iterator LLTextBase::getEditableSegIterContaining(S32 index) const
+{
+ segment_set_t::const_iterator it = getSegIterContaining(index);
+ segment_set_t::const_iterator orig_it = it;
+ if (it == mSegments.end()) return it;
+
+ if (!(*it)->canEdit()
+ && index == (*it)->getStart()
+ && it != mSegments.begin())
+ {
+ it--;
+ if ((*it)->canEdit())
+ {
+ return it;
+ }
+ }
+ return orig_it;
+}
+
LLTextBase::segment_set_t::iterator LLTextBase::getSegIterContaining(S32 index)
{
+
static LLPointer<LLIndexSegment> index_segment = new LLIndexSegment();
- if (index > getLength()) { return mSegments.end(); }
+ S32 text_len = 0;
+ if (!useLabel())
+ {
+ text_len = getLength();
+ }
+ else
+ {
+ text_len = mLabel.getWString().length();
+ }
+
+ if (index > text_len) { 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);
@@ -1508,7 +1877,17 @@ LLTextBase::segment_set_t::const_iterator LLTextBase::getSegIterContaining(S32 i
{
static LLPointer<LLIndexSegment> index_segment = new LLIndexSegment();
- if (index > getLength()) { return mSegments.end(); }
+ S32 text_len = 0;
+ if (!useLabel())
+ {
+ text_len = getLength();
+ }
+ else
+ {
+ text_len = mLabel.getWString().length();
+ }
+
+ if (index > text_len) { 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(); }
@@ -1558,16 +1937,34 @@ void LLTextBase::createUrlContextMenu(S32 x, S32 y, const std::string &in_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.Block", boost::bind(&LLUrlAction::blockObject, url));
registrar.add("Url.Teleport", boost::bind(&LLUrlAction::teleportToLocation, url));
registrar.add("Url.ShowProfile", boost::bind(&LLUrlAction::showProfile, url));
+ registrar.add("Url.AddFriend", boost::bind(&LLUrlAction::addFriend, url));
+ registrar.add("Url.RemoveFriend", boost::bind(&LLUrlAction::removeFriend, url));
+ registrar.add("Url.SendIM", boost::bind(&LLUrlAction::sendIM, url));
registrar.add("Url.ShowOnMap", boost::bind(&LLUrlAction::showLocationOnMap, url));
registrar.add("Url.CopyLabel", boost::bind(&LLUrlAction::copyLabelToClipboard, url));
registrar.add("Url.CopyUrl", boost::bind(&LLUrlAction::copyURLToClipboard, url));
// create and return the context menu from the XUI file
delete mPopupMenu;
+ llassert(LLMenuGL::sMenuContainer != NULL);
mPopupMenu = LLUICtrlFactory::getInstance()->createFromFile<LLContextMenu>(xui_file, LLMenuGL::sMenuContainer,
LLMenuHolderGL::child_registry_t::instance());
+ if (mIsFriendSignal)
+ {
+ bool isFriend = *(*mIsFriendSignal)(LLUUID(LLUrlAction::getUserID(url)));
+ LLView* addFriendButton = mPopupMenu->getChild<LLView>("add_friend");
+ LLView* removeFriendButton = mPopupMenu->getChild<LLView>("remove_friend");
+
+ if (addFriendButton && removeFriendButton)
+ {
+ addFriendButton->setEnabled(!isFriend);
+ removeFriendButton->setEnabled(isFriend);
+ }
+ }
+
if (mPopupMenu)
{
mPopupMenu->show(x, y);
@@ -1620,19 +2017,24 @@ static LLUIImagePtr image_from_icon_name(const std::string& icon_name)
}
}
+static LLTrace::BlockTimerStatHandle FTM_PARSE_HTML("Parse HTML");
+
+
+
void LLTextBase::appendTextImpl(const std::string &new_text, const LLStyle::Params& input_params)
{
LLStyle::Params style_params(input_params);
- style_params.fillFrom(getDefaultStyleParams());
+ style_params.fillFrom(getStyleParams());
S32 part = (S32)LLTextParser::WHOLE;
if (mParseHTML && !style_params.is_link) // Don't search for URLs inside a link segment (STORM-358).
{
+ LL_RECORD_BLOCK_TIME(FTM_PARSE_HTML);
S32 start=0,end=0;
LLUrlMatch match;
std::string text = new_text;
while ( LLUrlRegistry::instance().findUrl(text, match,
- boost::bind(&LLTextBase::replaceUrl, this, _1, _2, _3)) )
+ boost::bind(&LLTextBase::replaceUrl, this, _1, _2, _3),isContentTrusted()))
{
start = match.getStart();
end = match.getEnd()+1;
@@ -1656,21 +2058,30 @@ void LLTextBase::appendTextImpl(const std::string &new_text, const LLStyle::Para
appendAndHighlightText(subtext, part, style_params);
}
- // inserts an avatar icon preceding the Url if appropriate
- LLTextUtil::processUrlMatch(&match,this);
+ // add icon before url if need
+ LLTextUtil::processUrlMatch(&match, this, isContentTrusted() || match.isTrusted());
// output the styled Url
appendAndHighlightTextImpl(match.getLabel(), part, link_params, match.underlineOnHoverOnly());
+
+ // show query part of url with gray color only for LLUrlEntryHTTP and LLUrlEntryHTTPNoProtocol url entries
+ std::string label = match.getQuery();
+ if (label.size())
+ {
+ link_params.color = LLColor4::grey;
+ link_params.readonly_color = LLColor4::grey;
+ appendAndHighlightTextImpl(label, part, link_params, match.underlineOnHoverOnly());
+ }
// set the tooltip for the 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());
- }
+ {
+ LLTextSegmentPtr segment = *it;
+ segment->setToolTip(match.getTooltip());
+ }
}
// move on to the rest of the text after the Url
@@ -1696,8 +2107,11 @@ void LLTextBase::appendTextImpl(const std::string &new_text, const LLStyle::Para
}
}
+static LLTrace::BlockTimerStatHandle FTM_APPEND_TEXT("Append Text");
+
void LLTextBase::appendText(const std::string &new_text, bool prepend_newline, const LLStyle::Params& input_params)
{
+ LL_RECORD_BLOCK_TIME(FTM_APPEND_TEXT);
if (new_text.empty())
return;
@@ -1706,9 +2120,47 @@ void LLTextBase::appendText(const std::string &new_text, bool prepend_newline, c
appendTextImpl(new_text,input_params);
}
+void LLTextBase::setLabel(const LLStringExplicit& label)
+{
+ mLabel = label;
+ resetLabel();
+}
+
+BOOL LLTextBase::setLabelArg(const std::string& key, const LLStringExplicit& text )
+{
+ mLabel.setArg(key, text);
+ return TRUE;
+}
+
+void LLTextBase::resetLabel()
+{
+ if (useLabel())
+ {
+ clearSegments();
+
+ LLStyle* style = new LLStyle(getStyleParams());
+ style->setColor(mTentativeFgColor);
+ LLStyleConstSP sp(style);
+
+ LLTextSegmentPtr label = new LLLabelTextSegment(sp, 0, mLabel.getWString().length() + 1, *this);
+ insertSegment(label);
+ }
+}
+
+bool LLTextBase::useLabel() const
+{
+ return !getLength() && !mLabel.empty() && !hasFocus();
+}
+
+void LLTextBase::setFont(const LLFontGL* font)
+{
+ mFont = font;
+ mStyleDirty = true;
+}
+
void LLTextBase::needsReflow(S32 index)
{
- lldebugs << "reflow on object " << (void*)this << " index = " << mReflowIndex << ", new index = " << index << llendl;
+ LL_DEBUGS() << "reflow on object " << (void*)this << " index = " << mReflowIndex << ", new index = " << index << LL_ENDL;
mReflowIndex = llmin(mReflowIndex, index);
}
@@ -1926,7 +2378,7 @@ void LLTextBase::setWText(const LLWString& text)
const LLWString& LLTextBase::getWText() const
{
- return getViewModel()->getDisplay();
+ return getViewModel()->getDisplay();
}
// If round is true, if the position is on the right half of a character, the cursor
@@ -1936,18 +2388,19 @@ const LLWString& LLTextBase::getWText() const
S32 LLTextBase::getDocIndexFromLocalCoord( S32 local_x, S32 local_y, BOOL round, bool hit_past_end_of_line) const
{
// Figure out which line we're nearest to.
- LLRect visible_region = getVisibleDocumentRect();
+ LLRect doc_rect = mDocumentView->getRect();
+ S32 doc_y = local_y - doc_rect.mBottom;
// binary search for line that starts before local_y
- line_list_t::const_iterator line_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), local_y - mVisibleTextRect.mBottom + visible_region.mBottom, compare_bottom());
+ line_list_t::const_iterator line_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), doc_y, compare_bottom());
- if (line_iter == mLineInfoList.end())
+ if (!mLineInfoList.size() || line_iter == mLineInfoList.end())
{
return getLength(); // past the end
}
S32 pos = getLength();
- S32 start_x = mVisibleTextRect.mLeft + line_iter->mRect.mLeft - visible_region.mLeft;
+ S32 start_x = line_iter->mRect.mLeft + doc_rect.mLeft;
segment_set_t::iterator line_seg_iter;
S32 line_seg_offset;
@@ -1969,7 +2422,7 @@ S32 LLTextBase::getDocIndexFromLocalCoord( S32 local_x, S32 local_y, BOOL round,
}
// 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)
+ if (hit_past_end_of_line && doc_y > line_iter->mRect.mTop)
{
pos = segment_line_start;
break;
@@ -1998,11 +2451,19 @@ S32 LLTextBase::getDocIndexFromLocalCoord( S32 local_x, S32 local_y, BOOL round,
pos = segment_line_start + offset;
break;
}
- else if (hit_past_end_of_line && segmentp->getEnd() > line_iter->mDocIndexEnd - 1)
+ else if (hit_past_end_of_line && segmentp->getEnd() >= line_iter->mDocIndexEnd)
{
- // 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);
+ if (getLineNumFromDocIndex(line_iter->mDocIndexEnd - 1) == line_iter->mLineNum)
+ {
+ // if segment wraps to the next line we should step one char back
+ // to compensate for the space char between words
+ // which is removed due to wrapping
+ pos = llclamp(line_iter->mDocIndexEnd - 1, 0, getLength());
+ }
+ else
+ {
+ pos = llclamp(line_iter->mDocIndexEnd, 0, getLength());
+ }
break;
}
start_x += text_width;
@@ -2025,7 +2486,6 @@ LLRect LLTextBase::getDocRectFromDocIndex(S32 pos) const
// 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;
@@ -2085,7 +2545,7 @@ LLRect LLTextBase::getLocalRectFromDocIndex(S32 pos) const
{
// return default height rect in upper left
local_rect = content_window_rect;
- local_rect.mBottom = local_rect.mTop - (S32)(mDefaultFont->getLineHeight());
+ local_rect.mBottom = local_rect.mTop - mFont->getLineHeight();
return local_rect;
}
@@ -2191,21 +2651,24 @@ void LLTextBase::setCursorAtLocalPos( S32 local_x, S32 local_y, bool round, bool
void LLTextBase::changeLine( S32 delta )
{
S32 line = getLineNumFromDocIndex(mCursorPos);
+ S32 max_line_nb = getLineCount() - 1;
+ max_line_nb = (max_line_nb < 0 ? 0 : max_line_nb);
+
+ S32 new_line = llclamp(line + delta, 0, max_line_nb);
- 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);
+ if (new_line != line)
+ {
+ LLRect visible_region = getVisibleDocumentRect();
+ S32 new_cursor_pos = getDocIndexFromLocalCoord(mDesiredXPixel,
+ mLineInfoList[new_line].mRect.mBottom + mVisibleTextRect.mBottom - visible_region.mBottom, TRUE);
+ S32 actual_line = getLineNumFromDocIndex(new_cursor_pos);
+ if (actual_line != new_line)
+ {
+ // line edge, correcting position by 1 to move onto proper line
+ new_cursor_pos += new_line - actual_line;
+ }
+ setCursorPos(new_cursor_pos, true);
+ }
}
bool LLTextBase::scrolledToStart()
@@ -2308,6 +2771,9 @@ S32 LLTextBase::getEditableIndex(S32 index, bool increasing_direction)
void LLTextBase::updateRects()
{
+ LLRect old_text_rect = mVisibleTextRect;
+ mVisibleTextRect = mScroller ? mScroller->getContentWindowRect() : getLocalRect();
+
if (mLineInfoList.empty())
{
mTextBoundingRect = LLRect(0, mVPad, mHPad, 0);
@@ -2323,10 +2789,24 @@ void LLTextBase::updateRects()
}
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;
+ S32 delta_pos = 0;
+
+ switch(mVAlign)
+ {
+ case LLFontGL::TOP:
+ delta_pos = llmax(mVisibleTextRect.getHeight() - mTextBoundingRect.mTop, -mTextBoundingRect.mBottom);
+ break;
+ case LLFontGL::VCENTER:
+ delta_pos = (llmax(mVisibleTextRect.getHeight() - mTextBoundingRect.mTop, -mTextBoundingRect.mBottom) + (mVisibleTextRect.mBottom - mTextBoundingRect.mBottom)) / 2;
+ break;
+ case LLFontGL::BOTTOM:
+ delta_pos = mVisibleTextRect.mBottom - mTextBoundingRect.mBottom;
+ break;
+ case LLFontGL::BASELINE:
+ // do nothing
+ break;
+ }
// move line segments to fit new document rect
for (line_list_t::iterator it = mLineInfoList.begin(); it != mLineInfoList.end(); ++it)
{
@@ -2336,8 +2816,9 @@ void LLTextBase::updateRects()
}
// update document container dimensions according to text contents
- LLRect doc_rect = mTextBoundingRect;
+ LLRect doc_rect;
// use old mVisibleTextRect constraint document to width of viewable region
+ doc_rect.mBottom = llmin(mVisibleTextRect.mBottom, mTextBoundingRect.mBottom);
doc_rect.mLeft = 0;
// allow horizontal scrolling?
@@ -2347,11 +2828,22 @@ void LLTextBase::updateRects()
doc_rect.mRight = mScroller
? llmax(mVisibleTextRect.getWidth(), mTextBoundingRect.mRight)
: mVisibleTextRect.getWidth();
+ doc_rect.mTop = llmax(mVisibleTextRect.mTop, mTextBoundingRect.mTop);
if (!mScroller)
{
// push doc rect to top of text widget
- doc_rect.translate(0, mVisibleTextRect.getHeight() - doc_rect.mTop);
+ switch(mVAlign)
+ {
+ case LLFontGL::TOP:
+ doc_rect.translate(0, mVisibleTextRect.getHeight() - doc_rect.mTop);
+ break;
+ case LLFontGL::VCENTER:
+ doc_rect.translate(0, (mVisibleTextRect.getHeight() - doc_rect.mTop) / 2);
+ case LLFontGL::BOTTOM:
+ default:
+ break;
+ }
}
mDocumentView->setShape(doc_rect);
@@ -2359,7 +2851,6 @@ void LLTextBase::updateRects()
//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)
@@ -2371,10 +2862,59 @@ void LLTextBase::updateRects()
needsReflow();
}
+ // update mTextBoundingRect after mVisibleTextRect took scrolls into account
+ if (!mLineInfoList.empty() && mScroller)
+ {
+ S32 delta_pos = 0;
+
+ switch(mVAlign)
+ {
+ case LLFontGL::TOP:
+ delta_pos = llmax(mVisibleTextRect.getHeight() - mTextBoundingRect.mTop, -mTextBoundingRect.mBottom);
+ break;
+ case LLFontGL::VCENTER:
+ delta_pos = (llmax(mVisibleTextRect.getHeight() - mTextBoundingRect.mTop, -mTextBoundingRect.mBottom) + (mVisibleTextRect.mBottom - mTextBoundingRect.mBottom)) / 2;
+ break;
+ case LLFontGL::BOTTOM:
+ delta_pos = mVisibleTextRect.mBottom - mTextBoundingRect.mBottom;
+ break;
+ case LLFontGL::BASELINE:
+ // do nothing
+ break;
+ }
+ // move line segments to fit new visible rect
+ if (delta_pos != 0)
+ {
+ for (line_list_t::iterator it = mLineInfoList.begin(); it != mLineInfoList.end(); ++it)
+ {
+ it->mRect.translate(0, delta_pos);
+ }
+ mTextBoundingRect.translate(0, delta_pos);
+ }
+ }
+
// update document container again, using new mVisibleTextRect (that has scrollbars enabled as needed)
+ doc_rect.mBottom = llmin(mVisibleTextRect.mBottom, mTextBoundingRect.mBottom);
+ doc_rect.mLeft = 0;
doc_rect.mRight = mScroller
? llmax(mVisibleTextRect.getWidth(), mTextBoundingRect.mRight)
: mVisibleTextRect.getWidth();
+ doc_rect.mTop = llmax(mVisibleTextRect.getHeight(), mTextBoundingRect.getHeight()) + doc_rect.mBottom;
+ if (!mScroller)
+ {
+ // push doc rect to top of text widget
+ switch(mVAlign)
+ {
+ case LLFontGL::TOP:
+ doc_rect.translate(0, mVisibleTextRect.getHeight() - doc_rect.mTop);
+ break;
+ case LLFontGL::VCENTER:
+ doc_rect.translate(0, (mVisibleTextRect.getHeight() - doc_rect.mTop) / 2);
+ case LLFontGL::BOTTOM:
+ default:
+ break;
+ }
+ }
mDocumentView->setShape(doc_rect);
}
@@ -2405,10 +2945,37 @@ LLRect LLTextBase::getVisibleDocumentRect() const
{
return mScroller->getVisibleContentRect();
}
- else
+ else if (mClip)
{
- // entire document rect is visible when not scrolling
+ LLRect visible_text_rect = getVisibleTextRect();
+ LLRect doc_rect = mDocumentView->getRect();
+ visible_text_rect.translate(-doc_rect.mLeft, -doc_rect.mBottom);
+
+ // reject partially visible lines
+ LLRect visible_lines_rect;
+ for (line_list_t::const_iterator it = mLineInfoList.begin(), end_it = mLineInfoList.end();
+ it != end_it;
+ ++it)
+ {
+ bool line_visible = mClipPartial ? visible_text_rect.contains(it->mRect) : visible_text_rect.overlaps(it->mRect);
+ if (line_visible)
+ {
+ if (visible_lines_rect.isEmpty())
+ {
+ visible_lines_rect = it->mRect;
+ }
+ else
+ {
+ visible_lines_rect.unionWith(it->mRect);
+ }
+ }
+ }
+ return visible_lines_rect;
+ }
+ else
+ { // entire document rect is visible
// but offset according to height of widget
+
LLRect doc_rect = mDocumentView->getLocalRect();
doc_rect.mLeft -= mDocumentView->getRect().mLeft;
// adjust for height of text above widget baseline
@@ -2426,6 +2993,15 @@ boost::signals2::connection LLTextBase::setURLClickedCallback(const commit_signa
return mURLClickSignal->connect(cb);
}
+boost::signals2::connection LLTextBase::setIsFriendCallback(const is_friend_signal_t::slot_type& cb)
+{
+ if (!mIsFriendSignal)
+ {
+ mIsFriendSignal = new is_friend_signal_t();
+ }
+ return mIsFriendSignal->connect(cb);
+}
+
//
// LLTextSegment
//
@@ -2459,7 +3035,10 @@ 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 ""; }
+const std::string& LLTextSegment::getName() const
+{
+ return LLStringUtil::null;
+}
void LLTextSegment::onMouseCaptureLost() {}
void LLTextSegment::screenPointToLocal(S32 screen_x, S32 screen_y, S32* local_x, S32* local_y) const {}
void LLTextSegment::localPointToScreen(S32 local_x, S32 local_y, S32* screen_x, S32* screen_y) const {}
@@ -2475,7 +3054,7 @@ LLNormalTextSegment::LLNormalTextSegment( LLStyleConstSP style, S32 start, S32 e
mToken(NULL),
mEditor(editor)
{
- mFontHeight = llceil(mStyle->getFont()->getLineHeight());
+ mFontHeight = mStyle->getFont()->getLineHeight();
LLUIImagePtr image = mStyle->getImage();
if (image.notNull())
@@ -2491,7 +3070,7 @@ LLNormalTextSegment::LLNormalTextSegment( const LLColor4& color, S32 start, S32
{
mStyle = new LLStyle(LLStyle::Params().visible(is_visible).color(color));
- mFontHeight = llceil(mStyle->getFont()->getLineHeight());
+ mFontHeight = mStyle->getFont()->getLineHeight();
}
LLNormalTextSegment::~LLNormalTextSegment()
@@ -2514,7 +3093,7 @@ F32 LLNormalTextSegment::drawClippedSegment(S32 seg_start, S32 seg_end, S32 sele
{
F32 alpha = LLViewDrawContext::getCurrentContext().mAlpha;
- const LLWString &text = mEditor.getWText();
+ const LLWString &text = getWText();
F32 right_x = rect.mLeft;
if (!mStyle->isVisible())
@@ -2526,21 +3105,21 @@ F32 LLNormalTextSegment::drawClippedSegment(S32 seg_start, S32 seg_end, S32 sele
LLColor4 color = (mEditor.getReadOnly() ? mStyle->getReadOnlyColor() : mStyle->getColor()) % alpha;
- if( selection_start > seg_start )
+ 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,
+ color,
+ LLFontGL::LEFT, mEditor.mVAlign,
+ LLFontGL::NORMAL,
+ mStyle->getShadowType(),
+ length,
+ &right_x,
+ mEditor.getUseEllipses());
}
rect.mLeft = (S32)ceil(right_x);
@@ -2552,14 +3131,14 @@ F32 LLNormalTextSegment::drawClippedSegment(S32 seg_start, S32 seg_end, S32 sele
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,
+ 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 )
@@ -2569,14 +3148,14 @@ F32 LLNormalTextSegment::drawClippedSegment(S32 seg_start, S32 seg_end, S32 sele
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());
+ rect,
+ color,
+ LLFontGL::LEFT, mEditor.mVAlign,
+ LLFontGL::NORMAL,
+ mStyle->getShadowType(),
+ length,
+ &right_x,
+ mEditor.getUseEllipses());
}
return right_x;
}
@@ -2631,7 +3210,7 @@ BOOL LLNormalTextSegment::handleMouseUp(S32 x, S32 y, MASK mask)
// 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());
+ LLUrlAction::clickAction(getStyle()->getLinkHREF(), mEditor.isContentTrusted());
return TRUE;
}
}
@@ -2664,7 +3243,7 @@ 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;
+ LL_WARNS() << "LLTextSegment::setToolTip: cannot replace keyword tooltip." << LL_ENDL;
return;
}
mTooltip = tooltip;
@@ -2677,7 +3256,7 @@ bool LLNormalTextSegment::getDimensions(S32 first_char, S32 num_chars, S32& widt
if (num_chars > 0)
{
height = mFontHeight;
- const LLWString &text = mEditor.getWText();
+ const LLWString &text = getWText();
// if last character is a newline, then return true, forcing line break
width = mStyle->getFont()->getWidth(text.c_str(), mStart + first_char, num_chars);
}
@@ -2686,7 +3265,7 @@ bool LLNormalTextSegment::getDimensions(S32 first_char, S32 num_chars, S32& widt
S32 LLNormalTextSegment::getOffset(S32 segment_local_x_coord, S32 start_offset, S32 num_chars, bool round) const
{
- const LLWString &text = mEditor.getWText();
+ const LLWString &text = getWText();
return mStyle->getFont()->charFromPixelOffset(text.c_str(), mStart + start_offset,
(F32)segment_local_x_coord,
F32_MAX,
@@ -2696,7 +3275,7 @@ S32 LLNormalTextSegment::getOffset(S32 segment_local_x_coord, S32 start_offset,
S32 LLNormalTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars) const
{
- const LLWString &text = mEditor.getWText();
+ const LLWString &text = getWText();
LLUIImagePtr image = mStyle->getImage();
if( image.notNull())
@@ -2714,7 +3293,23 @@ S32 LLNormalTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 lin
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,
+
+
+ S32 offsetLength = text.length() - (segment_offset + mStart);
+
+ if(getLength() < segment_offset + mStart)
+ {
+ LL_INFOS() << "getLength() < segment_offset + mStart\t getLength()\t" << getLength() << "\tsegment_offset:\t"
+ << segment_offset << "\tmStart:\t" << mStart << "\tsegments\t" << mEditor.mSegments.size() << "\tmax_chars\t" << max_chars << LL_ENDL;
+ }
+
+ if( (offsetLength + 1) < max_chars)
+ {
+ LL_INFOS() << "offsetString.length() + 1 < max_chars\t max_chars:\t" << max_chars << "\toffsetString.length():\t" << offsetLength << " getLength() : "
+ << getLength() << "\tsegment_offset:\t" << segment_offset << "\tmStart:\t" << mStart << "\tsegments\t" << mEditor.mSegments.size() << LL_ENDL;
+ }
+
+ S32 num_chars = mStyle->getFont()->maxDrawableChars( text.c_str() + (segment_offset + mStart),
(F32)num_pixels,
max_chars,
word_wrap_style);
@@ -2732,7 +3327,7 @@ S32 LLNormalTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 lin
S32 last_char_in_run = mStart + segment_offset + num_chars;
// check length first to avoid indexing off end of string
if (last_char_in_run < mEnd
- && (last_char_in_run >= mEditor.getLength() ))
+ && (last_char_in_run >= getLength()))
{
num_chars++;
}
@@ -2741,13 +3336,46 @@ S32 LLNormalTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 lin
void LLNormalTextSegment::dump() const
{
- llinfos << "Segment [" <<
+ LL_INFOS() << "Segment [" <<
// mColor.mV[VX] << ", " <<
// mColor.mV[VY] << ", " <<
// mColor.mV[VZ] << "]\t[" <<
mStart << ", " <<
getEnd() << "]" <<
- llendl;
+ LL_ENDL;
+}
+
+/*virtual*/
+const LLWString& LLNormalTextSegment::getWText() const
+{
+ return mEditor.getWText();
+}
+
+/*virtual*/
+const S32 LLNormalTextSegment::getLength() const
+{
+ return mEditor.getLength();
+}
+
+LLLabelTextSegment::LLLabelTextSegment( LLStyleConstSP style, S32 start, S32 end, LLTextBase& editor )
+: LLNormalTextSegment(style, start, end, editor)
+{
+}
+
+LLLabelTextSegment::LLLabelTextSegment( const LLColor4& color, S32 start, S32 end, LLTextBase& editor, BOOL is_visible)
+: LLNormalTextSegment(color, start, end, editor, is_visible)
+{
+}
+
+/*virtual*/
+const LLWString& LLLabelTextSegment::getWText() const
+{
+ return mEditor.getWlabel();
+}
+/*virtual*/
+const S32 LLLabelTextSegment::getLength() const
+{
+ return mEditor.getWlabel().length();
}
//
@@ -2859,11 +3487,11 @@ LLLineBreakTextSegment::LLLineBreakTextSegment(S32 pos):LLTextSegment(pos,pos+1)
{
LLStyleSP s( new LLStyle(LLStyle::Params().visible(true)));
- mFontHeight = llceil(s->getFont()->getLineHeight());
+ mFontHeight = s->getFont()->getLineHeight();
}
LLLineBreakTextSegment::LLLineBreakTextSegment(LLStyleConstSP style,S32 pos):LLTextSegment(pos,pos+1)
{
- mFontHeight = llceil(style->getFont()->getLineHeight());
+ mFontHeight = style->getFont()->getLineHeight();
}
LLLineBreakTextSegment::~LLLineBreakTextSegment()
{
@@ -2900,7 +3528,7 @@ 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());;
+ height = mStyle->getFont()->getLineHeight();
LLUIImagePtr image = mStyle->getImage();
if( num_chars>0 && image.notNull())
@@ -2914,11 +3542,18 @@ bool LLImageTextSegment::getDimensions(S32 first_char, S32 num_chars, S32& width
S32 LLImageTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars) const
{
LLUIImagePtr image = mStyle->getImage();
+
+ if (image.isNull())
+ {
+ return 1;
+ }
+
S32 image_width = image->getWidth();
if(line_offset == 0 || num_pixels>image_width + IMAGE_HPAD)
{
return 1;
}
+
return 0;
}
@@ -2928,19 +3563,26 @@ F32 LLImageTextSegment::draw(S32 start, S32 end, S32 selection_start, S32 select
{
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;
+ if (image.notNull())
+ {
+ S32 style_image_height = image->getHeight();
+ S32 style_image_width = image->getWidth();
+ // Text is drawn from the top of the draw_rect downward
+
+ S32 text_center = draw_rect.mTop - (draw_rect.getHeight() / 2);
+ // Align image to center of draw rect
+ S32 image_bottom = text_center - (style_image_height / 2);
+ image->draw(draw_rect.mLeft, image_bottom,
+ style_image_width, style_image_height, color);
+
+ const S32 IMAGE_HPAD = 3;
+ return draw_rect.mLeft + style_image_width + IMAGE_HPAD;
+ }
}
return 0.0;
}
+void LLTextBase::setWordWrap(bool wrap)
+{
+ mWordWrap = wrap;
+}
diff --git a/indra/llui/lltextbase.h b/indra/llui/lltextbase.h
index aafcf8ceb0..dfc10923f3 100644..100755
--- a/indra/llui/lltextbase.h
+++ b/indra/llui/lltextbase.h
@@ -30,6 +30,7 @@
#include "v4color.h"
#include "lleditmenuhandler.h"
+#include "llspellcheckmenuhandler.h"
#include "llstyle.h"
#include "llkeywords.h"
#include "llpanel.h"
@@ -40,6 +41,7 @@
#include <boost/signals2.hpp>
+class LLScrollContainer;
class LLContextMenu;
class LLUrlMatch;
@@ -49,10 +51,15 @@ class LLUrlMatch;
/// 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
+class LLTextSegment
+: public LLRefCount,
+ public LLMouseHandler
{
public:
- LLTextSegment(S32 start, S32 end) : mStart(start), mEnd(end){};
+ 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;
@@ -84,16 +91,16 @@ public:
/*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*/ const std::string& getName() const;
/*virtual*/ void onMouseCaptureLost();
/*virtual*/ void screenPointToLocal(S32 screen_x, S32 screen_y, S32* local_x, S32* local_y) const;
/*virtual*/ void localPointToScreen(S32 local_x, S32 local_y, S32* screen_x, S32* screen_y) const;
/*virtual*/ BOOL hasMouseCapture();
- S32 getStart() const { return mStart; }
- void setStart(S32 start) { mStart = start; }
- S32 getEnd() const { return mEnd; }
- void setEnd( S32 end ) { mEnd = end; }
+ 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;
@@ -105,7 +112,7 @@ class LLNormalTextSegment : public LLTextSegment
public:
LLNormalTextSegment( LLStyleConstSP style, S32 start, S32 end, LLTextBase& editor );
LLNormalTextSegment( const LLColor4& color, S32 start, S32 end, LLTextBase& editor, BOOL is_visible = TRUE);
- ~LLNormalTextSegment();
+ virtual ~LLNormalTextSegment();
/*virtual*/ bool getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height) const;
/*virtual*/ S32 getOffset(S32 segment_local_x_coord, S32 start_offset, S32 num_chars, bool round) const;
@@ -130,6 +137,9 @@ public:
protected:
F32 drawClippedSegment(S32 seg_start, S32 seg_end, S32 selection_start, S32 selection_end, LLRect rect);
+ virtual const LLWString& getWText() const;
+ virtual const S32 getLength() const;
+
protected:
class LLTextBase& mEditor;
LLStyleConstSP mStyle;
@@ -139,6 +149,21 @@ protected:
boost::signals2::connection mImageLoadedConnection;
};
+// This text segment is the same as LLNormalTextSegment, the only difference
+// is that LLNormalTextSegment draws value of LLTextBase (LLTextBase::getWText()),
+// but LLLabelTextSegment draws label of the LLTextBase (LLTextBase::mLabel)
+class LLLabelTextSegment : public LLNormalTextSegment
+{
+public:
+ LLLabelTextSegment( LLStyleConstSP style, S32 start, S32 end, LLTextBase& editor );
+ LLLabelTextSegment( const LLColor4& color, S32 start, S32 end, LLTextBase& editor, BOOL is_visible = TRUE);
+
+protected:
+
+ /*virtual*/ const LLWString& getWText() const;
+ /*virtual*/ const S32 getLength() const;
+};
+
// Text segment that changes it's style depending of mouse pointer position ( is it inside or outside segment)
class LLOnHoverChangeableTextSegment : public LLNormalTextSegment
{
@@ -230,14 +255,17 @@ typedef LLPointer<LLTextSegment> LLTextSegmentPtr;
///
class LLTextBase
: public LLUICtrl,
- protected LLEditMenuHandler
+ protected LLEditMenuHandler,
+ public LLSpellCheckMenuHandler
{
public:
friend class LLTextSegment;
friend class LLNormalTextSegment;
friend class LLUICtrlFactory;
- struct LineSpacingParams : public LLInitParam::Choice<LineSpacingParams>
+ typedef boost::signals2::signal<bool (const LLUUID& user_id)> is_friend_signal_t;
+
+ struct LineSpacingParams : public LLInitParam::ChoiceBlock<LineSpacingParams>
{
Alternative<F32> multiple;
Alternative<S32> pixels;
@@ -249,6 +277,7 @@ public:
Optional<LLUIColor> cursor_color,
text_color,
text_readonly_color,
+ text_tentative_color,
bg_readonly_color,
bg_writeable_color,
bg_focus_color,
@@ -259,13 +288,16 @@ public:
border_visible,
track_end,
read_only,
+ spellcheck,
allow_scroll,
plain_text,
wrap,
use_ellipses,
parse_urls,
parse_highlights,
- clip_partial;
+ clip,
+ clip_partial,
+ trusted_content;
Optional<S32> v_pad,
h_pad;
@@ -301,7 +333,7 @@ public:
/*virtual*/ BOOL acceptsTextInput() const { return !mReadOnly; }
/*virtual*/ void setColor( const LLColor4& c );
virtual void setReadOnlyColor(const LLColor4 &c);
- virtual void handleVisibilityChange( BOOL new_visibility );
+ virtual void onVisibilityChange( BOOL new_visibility );
/*virtual*/ void setValue(const LLSD& value );
/*virtual*/ LLTextViewModel* getViewModel() const;
@@ -310,11 +342,35 @@ public:
/*virtual*/ BOOL canDeselect() const;
/*virtual*/ void deselect();
+ virtual void onFocusReceived();
+ virtual void onFocusLost();
+
+ // LLSpellCheckMenuHandler overrides
+ /*virtual*/ bool getSpellCheck() const;
+
+ /*virtual*/ const std::string& getSuggestion(U32 index) const;
+ /*virtual*/ U32 getSuggestionCount() const;
+ /*virtual*/ void replaceWithSuggestion(U32 index);
+
+ /*virtual*/ void addToDictionary();
+ /*virtual*/ bool canAddToDictionary() const;
+
+ /*virtual*/ void addToIgnore();
+ /*virtual*/ bool canAddToIgnore() const;
+
+ // Spell checking helper functions
+ std::string getMisspelledWord(U32 pos) const;
+ bool isMisspelledWord(U32 pos) const;
+ void onSpellCheckSettingsChange();
+
// used by LLTextSegment layout code
bool getWordWrap() { return mWordWrap; }
bool getUseEllipses() { return mUseEllipses; }
bool truncate(); // returns true of truncation occurred
+ bool isContentTrusted() {return mTrustedContent;}
+ void setContentTrusted(bool trusted_content) { mTrustedContent = trusted_content; }
+
// TODO: move into LLTextSegment?
void createUrlContextMenu(S32 x, S32 y, const std::string &url); // create a popup context menu for the given Url
@@ -329,6 +385,21 @@ public:
const LLWString& getWText() const;
void appendText(const std::string &new_text, bool prepend_newline, const LLStyle::Params& input_params = LLStyle::Params());
+
+ void setLabel(const LLStringExplicit& label);
+ virtual BOOL setLabelArg(const std::string& key, const LLStringExplicit& text );
+
+ const std::string& getLabel() { return mLabel.getString(); }
+ const LLWString& getWlabel() { return mLabel.getWString();}
+
+ /**
+ * If label is set, draws text label (which is LLLabelTextSegment)
+ * that is visible when no user text provided
+ */
+ void resetLabel();
+
+ void setFont(const LLFontGL* font);
+
// force reflow of text
void needsReflow(S32 index = 0);
@@ -338,7 +409,7 @@ public:
void addDocumentChild(LLView* view);
void removeDocumentChild(LLView* view);
const LLView* getDocumentView() const { return mDocumentView; }
- LLRect getVisibleTextRect() { return mVisibleTextRect; }
+ LLRect getVisibleTextRect() const { return mVisibleTextRect; }
LLRect getTextBoundingRect();
LLRect getVisibleDocumentRect() const;
@@ -368,17 +439,43 @@ public:
bool scrolledToStart();
bool scrolledToEnd();
- const LLFontGL* getDefaultFont() const { return mDefaultFont; }
+ const LLFontGL* getFont() const { return mFont; }
virtual void appendLineBreakSegment(const LLStyle::Params& style_params);
virtual void appendImageSegment(const LLStyle::Params& style_params);
virtual void appendWidget(const LLInlineViewSegment::Params& params, const std::string& text, bool allow_undo);
boost::signals2::connection setURLClickedCallback(const commit_signal_t::slot_type& cb);
+ boost::signals2::connection setIsFriendCallback(const is_friend_signal_t::slot_type& cb);
+
+ void setWordWrap(bool wrap);
+ LLScrollContainer* getScrollContainer() const { return mScroller; }
protected:
+ // protected member variables
+ // List of offsets and segment index of the start of each line. Always has at least one node (0).
+ struct line_info
+ {
+ line_info(S32 index_start, S32 index_end, LLRect rect, S32 line_num);
+ S32 mDocIndexStart;
+ S32 mDocIndexEnd;
+ LLRect mRect;
+ S32 mLineNum; // actual line count (ignoring soft newlines due to word wrap)
+ };
+ typedef std::vector<line_info> line_list_t;
+
// helper structs
- struct compare_bottom;
- struct compare_top;
+ struct compare_bottom
+ {
+ bool operator()(const S32& a, const line_info& b) const;
+ bool operator()(const line_info& a, const S32& b) const;
+ bool operator()(const line_info& a, const line_info& b) const;
+ };
+ struct compare_top
+ {
+ bool operator()(const S32& a, const line_info& b) const;
+ bool operator()(const line_info& a, const S32& b) const;
+ bool operator()(const line_info& a, const line_info& b) const;
+ };
struct line_end_compare;
typedef std::vector<LLTextSegmentPtr> segment_vec_t;
@@ -426,26 +523,16 @@ protected:
};
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 beforeValueChange();
virtual void onValueChange(S32 start, S32 end);
+ virtual bool useLabel() const;
// draw methods
- void drawSelectionBackground(); // draws the black box behind the selected text
+ virtual void drawSelectionBackground(); // draws the black box behind the selected text
void drawCursor();
void drawText();
@@ -460,13 +547,15 @@ protected:
void getSegmentAndOffset( S32 startpos, segment_set_t::const_iterator* seg_iter, S32* offsetp ) const;
void getSegmentAndOffset( S32 startpos, segment_set_t::iterator* seg_iter, S32* offsetp );
LLTextSegmentPtr getSegmentAtLocalPos( S32 x, S32 y, bool hit_past_end_of_line = true);
+ segment_set_t::iterator getEditableSegIterContaining(S32 index);
+ segment_set_t::const_iterator getEditableSegIterContaining(S32 index) const;
segment_set_t::iterator getSegIterContaining(S32 index);
segment_set_t::const_iterator getSegIterContaining(S32 index) const;
void clearSegments();
void createDefaultSegment();
virtual void updateSegments();
void insertSegment(LLTextSegmentPtr segment_to_insert);
- const LLStyle::Params& getDefaultStyleParams();
+ const LLStyle::Params& getStyleParams();
// manage lines
S32 getLineStart( S32 line ) const;
@@ -501,6 +590,7 @@ protected:
void appendTextImpl(const std::string &new_text, const LLStyle::Params& input_params = LLStyle::Params());
void appendAndHighlightTextImpl(const std::string &new_text, S32 highlight_part, const LLStyle::Params& style_params, bool underline_on_hover_only = false);
+ S32 normalizeUri(std::string& uri);
protected:
@@ -511,15 +601,16 @@ protected:
LLRect mTextBoundingRect;
// default text style
- LLStyle::Params mDefaultStyle;
+ LLStyle::Params mStyle;
bool mStyleDirty;
- const LLFontGL* const mDefaultFont; // font that is used when none specified, can only be set by constructor
- const LLFontGL::ShadowType mFontShadow; // shadow style, can only be set by constructor
+ const LLFontGL* mFont;
+ const LLFontGL::ShadowType mFontShadow;
// colors
LLUIColor mCursorColor;
LLUIColor mFgColor;
LLUIColor mReadOnlyFgColor;
+ LLUIColor mTentativeFgColor;
LLUIColor mWriteableBgColor;
LLUIColor mReadOnlyBgColor;
LLUIColor mFocusBgColor;
@@ -534,9 +625,18 @@ protected:
// selection
S32 mSelectionStart;
S32 mSelectionEnd;
+ LLTimer mTripleClickTimer;
BOOL mIsSelecting; // Are we in the middle of a drag-select?
+ // spell checking
+ bool mSpellCheck;
+ S32 mSpellCheckStart;
+ S32 mSpellCheckEnd;
+ LLTimer mSpellCheckTimer;
+ std::list<std::pair<U32, U32> > mMisspellRanges;
+ std::vector<std::string> mSuggestionList;
+
// configuration
S32 mHPad; // padding on left of text
S32 mVPad; // padding above text
@@ -552,14 +652,17 @@ protected:
bool mTrackEnd; // if true, keeps scroll position at end of document during resize
bool mReadOnly;
bool mBGVisible; // render background?
+ bool mClip; // clip text to widget rect
bool mClipPartial; // false if we show lines that are partially inside bounding rect
+ bool mTrustedContent; // if false, does not allow to execute SURL links from this editor
bool mPlainText; // didn't use Image or Icon segments
+ bool mAutoIndent;
S32 mMaxTextByteLength; // Maximum length mText is allowed to be in bytes
// support widgets
LLContextMenu* mPopupMenu;
LLView* mDocumentView;
- class LLScrollContainer* mScroller;
+ LLScrollContainer* mScroller;
// transient state
S32 mReflowIndex; // index at which to start reflow. S32_MAX indicates no reflow needed.
@@ -569,6 +672,10 @@ protected:
// Fired when a URL link is clicked
commit_signal_t* mURLClickSignal;
+ // Used to check if user with given ID is avatar's friend
+ is_friend_signal_t* mIsFriendSignal;
+
+ LLUIString mLabel; // text label that is visible when no user text provided
};
#endif
diff --git a/indra/llui/lltextbox.cpp b/indra/llui/lltextbox.cpp
index 6a905b7ec0..d175204e6d 100644..100755
--- a/indra/llui/lltextbox.cpp
+++ b/indra/llui/lltextbox.cpp
@@ -60,10 +60,13 @@ BOOL LLTextBox::handleMouseDown(S32 x, S32 y, MASK mask)
if (!handled && mClickedCallback)
{
+ handled = TRUE;
+ }
+
+ if (handled)
+ {
// Route future Mouse messages here preemptively. (Release on mouse up.)
gFocusMgr.setMouseCapture( this );
-
- handled = TRUE;
}
return handled;
@@ -71,7 +74,7 @@ BOOL LLTextBox::handleMouseDown(S32 x, S32 y, MASK mask)
BOOL LLTextBox::handleMouseUp(S32 x, S32 y, MASK mask)
{
- BOOL handled = FALSE;
+ BOOL handled = LLTextBase::handleMouseUp(x, y, mask);
if (getSoundFlags() & MOUSE_UP)
{
@@ -93,10 +96,6 @@ BOOL LLTextBox::handleMouseUp(S32 x, S32 y, MASK mask)
handled = TRUE;
}
}
- else
- {
- handled = LLTextBase::handleMouseUp(x, y, mask);
- }
return handled;
}
@@ -150,7 +149,7 @@ S32 LLTextBox::getTextPixelHeight()
LLSD LLTextBox::getValue() const
{
- return LLSD(getText());
+ return getViewModel()->getValue();
}
BOOL LLTextBox::setTextArg( const std::string& key, const LLStringExplicit& text )
diff --git a/indra/llui/lltextbox.h b/indra/llui/lltextbox.h
index 071e18c638..071e18c638 100644..100755
--- a/indra/llui/lltextbox.h
+++ b/indra/llui/lltextbox.h
diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp
index 5a46c7c98e..926326aaff 100644..100755
--- a/indra/llui/lltexteditor.cpp
+++ b/indra/llui/lltexteditor.cpp
@@ -54,6 +54,7 @@
#include "llwindow.h"
#include "lltextparser.h"
#include "llscrollcontainer.h"
+#include "llspellcheck.h"
#include "llpanel.h"
#include "llurlregistry.h"
#include "lltooltip.h"
@@ -74,9 +75,8 @@ template class LLTextEditor* LLView::getChild<class LLTextEditor>(
//
// Constants
//
-const S32 UI_TEXTEDITOR_LINE_NUMBER_MARGIN = 32;
-const S32 UI_TEXTEDITOR_LINE_NUMBER_DIGITS = 4;
const S32 SPACES_PER_TAB = 4;
+const F32 SPELLCHECK_DELAY = 0.5f; // delay between the last keypress and spell checking the word the cursor is on
///////////////////////////////////////////////////////////////////
@@ -234,21 +234,23 @@ LLTextEditor::Params::Params()
prevalidate_callback("prevalidate_callback"),
embedded_items("embedded_items", false),
ignore_tab("ignore_tab", true),
- show_line_numbers("show_line_numbers", false),
+ auto_indent("auto_indent", true),
default_color("default_color"),
commit_on_focus_lost("commit_on_focus_lost", false),
- show_context_menu("show_context_menu")
+ show_context_menu("show_context_menu"),
+ enable_tooltip_paste("enable_tooltip_paste")
{
addSynonym(prevalidate_callback, "text_type");
}
LLTextEditor::LLTextEditor(const LLTextEditor::Params& p) :
LLTextBase(p),
+ mAutoreplaceCallback(),
mBaseDocIsPristine(TRUE),
mPristineCmd( NULL ),
mLastCmd( NULL ),
- mDefaultColor( p.default_color() ),
- mShowLineNumbers ( p.show_line_numbers ),
+ mDefaultColor( p.default_color() ),
+ mAutoIndent(p.auto_indent),
mCommitOnFocusLost( p.commit_on_focus_lost),
mAllowEmbeddedItems( p.embedded_items ),
mMouseDownX(0),
@@ -256,7 +258,10 @@ LLTextEditor::LLTextEditor(const LLTextEditor::Params& p) :
mTabsToNextField(p.ignore_tab),
mPrevalidateFunc(p.prevalidate_callback()),
mContextMenu(NULL),
- mShowContextMenu(p.show_context_menu)
+ mShowContextMenu(p.show_context_menu),
+ mEnableTooltipPaste(p.enable_tooltip_paste),
+ mPassDelete(FALSE),
+ mKeepSelectionOnReturn(false)
{
mSourceID.generate();
@@ -269,14 +274,7 @@ LLTextEditor::LLTextEditor(const LLTextEditor::Params& p) :
params.visible = p.border_visible;
mBorder = LLUICtrlFactory::create<LLViewBorder> (params);
addChild( mBorder );
-
setText(p.default_text());
-
- if (mShowLineNumbers)
- {
- mHPad += UI_TEXTEDITOR_LINE_NUMBER_MARGIN;
- updateRects();
- }
mParseOnTheFly = TRUE;
}
@@ -302,7 +300,7 @@ LLTextEditor::~LLTextEditor()
// Scrollbar is deleted by LLView
std::for_each(mUndoStack.begin(), mUndoStack.end(), DeletePointer());
-
+ mUndoStack.clear();
// context menu is owned by menu holder, not us
//delete mContextMenu;
}
@@ -592,6 +590,10 @@ void LLTextEditor::indentSelectedLines( S32 spaces )
}
}
+ // Disabling parsing on the fly to avoid updating text segments
+ // until all indentation commands are executed.
+ mParseOnTheFly = FALSE;
+
// Find each start-of-line and indent it
do
{
@@ -617,6 +619,8 @@ void LLTextEditor::indentSelectedLines( S32 spaces )
}
while( cur < right );
+ mParseOnTheFly = TRUE;
+
if( (right < getLength()) && (text[right] == '\n') )
{
right++;
@@ -652,6 +656,14 @@ void LLTextEditor::selectAll()
updatePrimary();
}
+void LLTextEditor::selectByCursorPosition(S32 prev_cursor_pos, S32 next_cursor_pos)
+{
+ setCursorPos(prev_cursor_pos);
+ startSelection();
+ setCursorPos(next_cursor_pos);
+ endSelection();
+}
+
BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask)
{
BOOL handled = FALSE;
@@ -699,7 +711,6 @@ BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask)
setCursorAtLocalPos( x, y, true );
startSelection();
}
- gFocusMgr.setMouseCapture( this );
}
handled = TRUE;
@@ -708,6 +719,10 @@ BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask)
// Delay cursor flashing
resetCursorBlink();
+ if (handled && !gFocusMgr.getMouseCapture())
+ {
+ gFocusMgr.setMouseCapture( this );
+ }
return handled;
}
@@ -767,7 +782,7 @@ BOOL LLTextEditor::handleHover(S32 x, S32 y, MASK mask)
setCursorAtLocalPos( clamped_x, clamped_y, true );
mSelectionEnd = mCursorPos;
}
- lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (active)" << llendl;
+ LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (active)" << LL_ENDL;
getWindow()->setCursor(UI_CURSOR_IBEAM);
handled = TRUE;
}
@@ -799,7 +814,7 @@ BOOL LLTextEditor::handleMouseUp(S32 x, S32 y, MASK mask)
BOOL handled = FALSE;
// if I'm not currently selecting text
- if (!(hasSelection() && hasMouseCapture()))
+ if (!(mIsSelecting && hasMouseCapture()))
{
// let text segments handle mouse event
handled = LLTextBase::handleMouseUp(x, y, mask);
@@ -942,12 +957,18 @@ S32 LLTextEditor::insert(S32 pos, const LLWString &wstr, bool group_with_next_op
S32 LLTextEditor::remove(S32 pos, S32 length, bool group_with_next_op)
{
S32 end_pos = getEditableIndex(pos + length, true);
+ BOOL removedChar = FALSE;
segment_vec_t segments_to_remove;
// store text segments
getSegmentsInRange(segments_to_remove, pos, pos + length, false);
+
+ if(pos <= end_pos)
+ {
+ removedChar = execute( new TextCmdRemove( pos, group_with_next_op, end_pos - pos, segments_to_remove ) );
+ }
- return execute( new TextCmdRemove( pos, group_with_next_op, end_pos - pos, segments_to_remove ) );
+ return removedChar;
}
S32 LLTextEditor::overwriteChar(S32 pos, llwchar wc)
@@ -1086,8 +1107,26 @@ void LLTextEditor::addChar(llwchar wc)
}
setCursorPos(mCursorPos + addChar( mCursorPos, wc ));
+
+ if (!mReadOnly && mAutoreplaceCallback != NULL)
+ {
+ // autoreplace the text, if necessary
+ S32 replacement_start;
+ S32 replacement_length;
+ LLWString replacement_string;
+ S32 new_cursor_pos = mCursorPos;
+ mAutoreplaceCallback(replacement_start, replacement_length, replacement_string, new_cursor_pos, getWText());
+
+ if (replacement_length > 0 || !replacement_string.empty())
+ {
+ remove(replacement_start, replacement_length, true);
+ insert(replacement_start, replacement_string, false, LLTextSegmentPtr());
+ setCursorPos(new_cursor_pos);
+ }
+ }
}
-void LLTextEditor::addLineBreakChar()
+
+void LLTextEditor::addLineBreakChar(BOOL group_together)
{
if( !getEnabled() )
{
@@ -1105,7 +1144,7 @@ void LLTextEditor::addLineBreakChar()
LLStyleConstSP sp(new LLStyle(LLStyle::Params()));
LLTextSegmentPtr segment = new LLLineBreakTextSegment(sp, mCursorPos);
- S32 pos = execute(new TextCmdAddChar(mCursorPos, FALSE, '\n', segment));
+ S32 pos = execute(new TextCmdAddChar(mCursorPos, group_together, '\n', segment));
setCursorPos(mCursorPos + pos);
}
@@ -1326,7 +1365,7 @@ void LLTextEditor::cut()
}
S32 left_pos = llmin( mSelectionStart, mSelectionEnd );
S32 length = llabs( mSelectionStart - mSelectionEnd );
- gClipboard.copyFromSubstring( getWText(), left_pos, length, mSourceID );
+ LLClipboard::instance().copyToClipboard( getWText(), left_pos, length);
deleteSelection( FALSE );
onKeyStroke();
@@ -1346,12 +1385,12 @@ void LLTextEditor::copy()
}
S32 left_pos = llmin( mSelectionStart, mSelectionEnd );
S32 length = llabs( mSelectionStart - mSelectionEnd );
- gClipboard.copyFromSubstring(getWText(), left_pos, length, mSourceID);
+ LLClipboard::instance().copyToClipboard(getWText(), left_pos, length);
}
BOOL LLTextEditor::canPaste() const
{
- return !mReadOnly && gClipboard.canPasteString();
+ return !mReadOnly && LLClipboard::instance().isTextAvailable();
}
// paste from clipboard
@@ -1387,16 +1426,8 @@ void LLTextEditor::pasteHelper(bool is_primary)
return;
}
- LLUUID source_id;
LLWString paste;
- if (is_primary)
- {
- paste = gClipboard.getPastePrimaryWString(&source_id);
- }
- else
- {
- paste = gClipboard.getPasteWString(&source_id);
- }
+ LLClipboard::instance().pasteFromClipboard(paste, is_primary);
if (paste.empty())
{
@@ -1411,6 +1442,23 @@ void LLTextEditor::pasteHelper(bool is_primary)
// Clean up string (replace tabs and remove characters that our fonts don't support).
LLWString clean_string(paste);
+ cleanStringForPaste(clean_string);
+
+ // Insert the new text into the existing text.
+
+ //paste text with linebreaks.
+ pasteTextWithLinebreaks(clean_string);
+
+ deselect();
+
+ onKeyStroke();
+ mParseOnTheFly = TRUE;
+}
+
+
+// Clean up string (replace tabs and remove characters that our fonts don't support).
+void LLTextEditor::cleanStringForPaste(LLWString & clean_string)
+{
LLWStringUtil::replaceTabsWithSpaces(clean_string, SPACES_PER_TAB);
if( mAllowEmbeddedItems )
{
@@ -1429,37 +1477,38 @@ void LLTextEditor::pasteHelper(bool is_primary)
}
}
}
+}
- // Insert the new text into the existing text.
- //paste text with linebreaks.
+void LLTextEditor::pasteTextWithLinebreaks(LLWString & clean_string)
+{
std::basic_string<llwchar>::size_type start = 0;
std::basic_string<llwchar>::size_type pos = clean_string.find('\n',start);
- while(pos!=-1)
+ while((pos != -1) && (pos != clean_string.length() -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()));
+ setCursorPos(mCursorPos + insert(mCursorPos, str, TRUE, LLTextSegmentPtr()));
}
- addLineBreakChar();
-
+ addLineBreakChar(TRUE); // Add a line break and group with the next addition.
+
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();
-
- onKeyStroke();
- mParseOnTheFly = TRUE;
+ if (pos != start)
+ {
+ std::basic_string<llwchar> str = std::basic_string<llwchar>(clean_string,start,clean_string.length()-start);
+ setCursorPos(mCursorPos + insert(mCursorPos, str, FALSE, LLTextSegmentPtr()));
+ }
+ else
+ {
+ addLineBreakChar(FALSE); // Add a line break and end the grouping.
+ }
}
-
-
// copy selection to primary
void LLTextEditor::copyPrimary()
{
@@ -1469,12 +1518,12 @@ void LLTextEditor::copyPrimary()
}
S32 left_pos = llmin( mSelectionStart, mSelectionEnd );
S32 length = llabs( mSelectionStart - mSelectionEnd );
- gClipboard.copyFromPrimarySubstring(getWText(), left_pos, length, mSourceID);
+ LLClipboard::instance().copyToClipboard(getWText(), left_pos, length, true);
}
BOOL LLTextEditor::canPastePrimary() const
{
- return !mReadOnly && gClipboard.canPastePrimaryString();
+ return !mReadOnly && LLClipboard::instance().isTextAvailable(true);
}
void LLTextEditor::updatePrimary()
@@ -1560,7 +1609,7 @@ BOOL LLTextEditor::handleControlKey(const KEY key, const MASK mask)
}
}
- if (handled)
+ if (handled && !gFocusMgr.getMouseCapture())
{
updatePrimary();
}
@@ -1604,11 +1653,14 @@ BOOL LLTextEditor::handleSpecialKey(const KEY key, const MASK mask)
case KEY_RETURN:
if (mask == MASK_NONE)
{
- if( hasSelection() )
+ if( hasSelection() && !mKeepSelectionOnReturn )
{
deleteSelection(FALSE);
}
- autoIndent(); // TODO: make this optional
+ if (mAutoIndent)
+ {
+ autoIndent();
+ }
}
else
{
@@ -1680,19 +1732,50 @@ BOOL LLTextEditor::handleKeyHere(KEY key, MASK mask )
{
return FALSE;
}
-
+
if (mReadOnly && mScroller)
{
handled = (mScroller && mScroller->handleKeyHere( key, mask ))
|| handleSelectionKey(key, mask)
|| handleControlKey(key, mask);
+ }
+ else
+ {
+ if (mEnableTooltipPaste &&
+ LLToolTipMgr::instance().toolTipVisible() &&
+ KEY_TAB == key)
+ { // Paste the first line of a tooltip into the editor
+ std::string message;
+ LLToolTipMgr::instance().getToolTipMessage(message);
+ LLWString tool_tip_text(utf8str_to_wstring(message));
+
+ if (tool_tip_text.size() > 0)
+ {
+ // Delete any selected characters (the tooltip text replaces them)
+ if(hasSelection())
+ {
+ deleteSelection(TRUE);
+ }
+
+ std::basic_string<llwchar>::size_type pos = tool_tip_text.find('\n',0);
+ if (pos != -1)
+ { // Extract the first line of the tooltip
+ tool_tip_text = std::basic_string<llwchar>(tool_tip_text, 0, pos);
+ }
+
+ // Add the text
+ cleanStringForPaste(tool_tip_text);
+ pasteTextWithLinebreaks(tool_tip_text);
+ handled = TRUE;
+ }
+ }
+ else
+ { // Normal key handling
+ handled = handleNavigationKey( key, mask )
+ || handleSelectionKey(key, mask)
+ || handleControlKey(key, mask)
+ || handleSpecialKey(key, mask);
}
- else
- {
- handled = handleNavigationKey( key, mask )
- || handleSelectionKey(key, mask)
- || handleControlKey(key, mask)
- || handleSpecialKey(key, mask);
}
if( handled )
@@ -1748,7 +1831,7 @@ BOOL LLTextEditor::handleUnicodeCharHere(llwchar uni_char)
// virtual
BOOL LLTextEditor::canDoDelete() const
{
- return !mReadOnly && ( hasSelection() || (mCursorPos < getLength()) );
+ return !mReadOnly && ( !mPassDelete || ( hasSelection() || (mCursorPos < getLength())) );
}
void LLTextEditor::doDelete()
@@ -1889,8 +1972,7 @@ void LLTextEditor::onFocusReceived()
updateAllowingLanguageInput();
}
-// virtual, from LLView
-void LLTextEditor::onFocusLost()
+void LLTextEditor::focusLostHelper()
{
updateAllowingLanguageInput();
@@ -1907,7 +1989,11 @@ void LLTextEditor::onFocusLost()
// Make sure cursor is shown again
getWindow()->showCursorFromMouseMove();
+}
+void LLTextEditor::onFocusLost()
+{
+ focusLostHelper();
LLTextBase::onFocusLost();
}
@@ -1934,6 +2020,7 @@ void LLTextEditor::showContextMenu(S32 x, S32 y)
{
if (!mContextMenu)
{
+ llassert(LLMenuGL::sMenuContainer != NULL);
mContextMenu = LLUICtrlFactory::instance().createFromFile<LLContextMenu>("menu_text_editor.xml",
LLMenuGL::sMenuContainer,
LLMenuHolderGL::child_registry_t::instance());
@@ -1955,7 +2042,38 @@ void LLTextEditor::showContextMenu(S32 x, S32 y)
S32 screen_x, screen_y;
localPointToScreen(x, y, &screen_x, &screen_y);
- mContextMenu->show(screen_x, screen_y);
+
+ setCursorAtLocalPos(x, y, false);
+ if (hasSelection())
+ {
+ if ( (mCursorPos < llmin(mSelectionStart, mSelectionEnd)) || (mCursorPos > llmax(mSelectionStart, mSelectionEnd)) )
+ {
+ deselect();
+ }
+ else
+ {
+ setCursorPos(llmax(mSelectionStart, mSelectionEnd));
+ }
+ }
+
+ bool use_spellcheck = getSpellCheck(), is_misspelled = false;
+ if (use_spellcheck)
+ {
+ mSuggestionList.clear();
+
+ // If the cursor is on a misspelled word, retrieve suggestions for it
+ std::string misspelled_word = getMisspelledWord(mCursorPos);
+ if ((is_misspelled = !misspelled_word.empty()) == true)
+ {
+ LLSpellChecker::instance().getSuggestions(misspelled_word, mSuggestionList);
+ }
+ }
+
+ mContextMenu->setItemVisible("Suggestion Separator", (use_spellcheck) && (!mSuggestionList.empty()));
+ mContextMenu->setItemVisible("Add to Dictionary", (use_spellcheck) && (is_misspelled));
+ mContextMenu->setItemVisible("Add to Ignore", (use_spellcheck) && (is_misspelled));
+ mContextMenu->setItemVisible("Spellcheck Separator", (use_spellcheck) && (is_misspelled));
+ mContextMenu->show(screen_x, screen_y, this);
}
@@ -1986,7 +2104,7 @@ void LLTextEditor::drawPreeditMarker()
return;
}
- const S32 line_height = llround( mDefaultFont->getLineHeight() );
+ const S32 line_height = mFont->getLineHeight();
S32 line_start = getLineStart(cur_line);
S32 line_y = mVisibleTextRect.mTop - line_height;
@@ -2022,36 +2140,41 @@ void LLTextEditor::drawPreeditMarker()
continue;
}
- S32 preedit_left = mVisibleTextRect.mLeft;
+ line_info& line = mLineInfoList[cur_line];
+ LLRect text_rect(line.mRect);
+ text_rect.mRight = mDocumentView->getRect().getWidth(); // clamp right edge to document extents
+ text_rect.translate(mDocumentView->getRect().mLeft, mDocumentView->getRect().mBottom); // adjust by scroll position
+
+ S32 preedit_left = text_rect.mLeft;
if (left > line_start)
{
- preedit_left += mDefaultFont->getWidth(text, line_start, left - line_start);
+ preedit_left += mFont->getWidth(text, line_start, left - line_start);
}
- S32 preedit_right = mVisibleTextRect.mLeft;
+ S32 preedit_right = text_rect.mLeft;
if (right < line_end)
{
- preedit_right += mDefaultFont->getWidth(text, line_start, right - line_start);
+ preedit_right += mFont->getWidth(text, line_start, right - line_start);
}
else
{
- preedit_right += mDefaultFont->getWidth(text, line_start, line_end - line_start);
+ preedit_right += mFont->getWidth(text, line_start, line_end - line_start);
}
if (mPreeditStandouts[i])
{
gl_rect_2d(preedit_left + preedit_standout_gap,
- line_y + preedit_standout_position,
- preedit_right - preedit_standout_gap - 1,
- line_y + preedit_standout_position - preedit_standout_thickness,
- (mCursorColor.get() * preedit_standout_brightness + mWriteableBgColor.get() * (1 - preedit_standout_brightness)).setAlpha(1.0f));
+ text_rect.mBottom + mFont->getDescenderHeight() - 1,
+ preedit_right - preedit_standout_gap - 1,
+ text_rect.mBottom + mFont->getDescenderHeight() - 1 - preedit_standout_thickness,
+ (mCursorColor.get() * preedit_standout_brightness + mWriteableBgColor.get() * (1 - preedit_standout_brightness)).setAlpha(1.0f));
}
else
{
gl_rect_2d(preedit_left + preedit_marker_gap,
- line_y + preedit_marker_position,
- preedit_right - preedit_marker_gap - 1,
- line_y + preedit_marker_position - preedit_marker_thickness,
- (mCursorColor.get() * preedit_marker_brightness + mWriteableBgColor.get() * (1 - preedit_marker_brightness)).setAlpha(1.0f));
+ text_rect.mBottom + mFont->getDescenderHeight() - 1,
+ preedit_right - preedit_marker_gap - 1,
+ text_rect.mBottom + mFont->getDescenderHeight() - 1 - preedit_marker_thickness,
+ (mCursorColor.get() * preedit_marker_brightness + mWriteableBgColor.get() * (1 - preedit_marker_brightness)).setAlpha(1.0f));
}
}
}
@@ -2063,69 +2186,6 @@ void LLTextEditor::drawPreeditMarker()
}
}
-
-void LLTextEditor::drawLineNumbers()
-{
- LLGLSUIDefault gls_ui;
- LLRect scrolled_view_rect = getVisibleDocumentRect();
- LLRect content_rect = getVisibleTextRect();
- LLLocalClipRect clip(content_rect);
- S32 first_line = getFirstVisibleLine();
- S32 num_lines = getLineCount();
- if (first_line >= num_lines)
- {
- return;
- }
-
- S32 cursor_line = mLineInfoList[getLineNumFromDocIndex(mCursorPos)].mLineNum;
-
- if (mShowLineNumbers)
- {
- S32 left = 0;
- S32 top = getRect().getHeight();
- S32 bottom = 0;
-
- 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;
-
- for (S32 cur_line = first_line; cur_line < num_lines; cur_line++)
- {
- line_info& line = mLineInfoList[cur_line];
-
- if ((line.mRect.mTop - scrolled_view_rect.mBottom) < mVisibleTextRect.mBottom)
- {
- break;
- }
-
- 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;
- }
- }
- }
-}
-
void LLTextEditor::draw()
{
{
@@ -2134,11 +2194,11 @@ void LLTextEditor::draw()
LLRect clip_rect(mVisibleTextRect);
clip_rect.stretch(1);
LLLocalClipRect clip(clip_rect);
- drawPreeditMarker();
}
LLTextBase::draw();
- drawLineNumbers();
+
+ drawPreeditMarker();
//RN: the decision was made to always show the orange border for keyboard focus but do not put an insertion caret
// when in readonly mode
@@ -2204,7 +2264,8 @@ void LLTextEditor::autoIndent()
S32 i;
LLWString text = getWText();
- while( ' ' == text[line_start] )
+ S32 offset = getLineOffsetFromDocIndex(mCursorPos);
+ while(( ' ' == text[line_start] ) && (space_count < offset))
{
space_count++;
line_start++;
@@ -2244,6 +2305,22 @@ void LLTextEditor::insertText(const std::string &new_text)
setEnabled( enabled );
}
+void LLTextEditor::insertText(LLWString &new_text)
+{
+ BOOL enabled = getEnabled();
+ setEnabled( TRUE );
+
+ // Delete any selected characters (the insertion replaces them)
+ if( hasSelection() )
+ {
+ deleteSelection(TRUE);
+ }
+
+ setCursorPos(mCursorPos + insert( mCursorPos, new_text, FALSE, LLTextSegmentPtr() ));
+
+ setEnabled( enabled );
+}
+
void LLTextEditor::appendWidget(const LLInlineViewSegment::Params& params, const std::string& text, bool allow_undo)
{
// Save old state
@@ -2361,54 +2438,6 @@ BOOL LLTextEditor::tryToRevertToPristineState()
return isPristine(); // TRUE => success
}
-
-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());
- for(S32 i = 0; i < count; i++)
- {
- 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);
-
- 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 (mReflowIndex < S32_MAX && mKeywords.isLoaded() && mParseOnTheFly)
- {
- LLFastTimer ft(FTM_SYNTAX_HIGHLIGHTING);
- // HACK: No non-ascii keywords for now
- segment_vec_t segment_list;
- mKeywords.findSegments(&segment_list, getWText(), mDefaultColor.get(), *this);
-
- 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)
- {
- insertSegment(*list_it);
- }
- }
-
- LLTextBase::updateSegments();
-}
-
void LLTextEditor::updateLinkSegments()
{
LLWString wtext = getWText();
@@ -2419,12 +2448,30 @@ void LLTextEditor::updateLinkSegments()
LLTextSegment *segment = *it;
if (segment && segment->getStyle() && segment->getStyle()->isLink())
{
- // 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());
+
+ segment_set_t::const_iterator next_it = mSegments.upper_bound(segment);
+ LLTextSegment *next_segment = *next_it;
+ if (next_segment)
+ {
+ LLWString next_url_label = wtext.substr(next_segment->getStart(), next_segment->getEnd()-next_segment->getStart());
+ std::string link_check = wstring_to_utf8str(url_label) + wstring_to_utf8str(next_url_label);
+ LLUrlMatch match;
+
+ if ( LLUrlRegistry::instance().findUrl(link_check, match))
+ {
+ if(match.getQuery() == wstring_to_utf8str(next_url_label))
+ {
+ continue;
+ }
+ }
+ }
+
+ // if the link's label (what the user can edit) is a valid Url,
+ // then update the link's HREF to be the same as the label text.
+ // This lets users edit Urls in-place.
if (LLUrlRegistry::instance().hasUrl(url_label))
{
std::string new_url = wstring_to_utf8str(url_label);
@@ -2465,20 +2512,20 @@ BOOL LLTextEditor::importBuffer(const char* buffer, S32 length )
instream.getline(tbuf, MAX_STRING);
if( 1 != sscanf(tbuf, "Linden text version %d", &version) )
{
- llwarns << "Invalid Linden text file header " << llendl;
+ LL_WARNS() << "Invalid Linden text file header " << LL_ENDL;
return FALSE;
}
if( 1 != version )
{
- llwarns << "Invalid Linden text file version: " << version << llendl;
+ LL_WARNS() << "Invalid Linden text file version: " << version << LL_ENDL;
return FALSE;
}
instream.getline(tbuf, MAX_STRING);
if( 0 != sscanf(tbuf, "{") )
{
- llwarns << "Invalid Linden text file format" << llendl;
+ LL_WARNS() << "Invalid Linden text file format" << LL_ENDL;
return FALSE;
}
@@ -2486,13 +2533,13 @@ BOOL LLTextEditor::importBuffer(const char* buffer, S32 length )
instream.getline(tbuf, MAX_STRING);
if( 1 != sscanf(tbuf, "Text length %d", &text_len) )
{
- llwarns << "Invalid Linden text length field" << llendl;
+ LL_WARNS() << "Invalid Linden text length field" << LL_ENDL;
return FALSE;
}
if( text_len > mMaxTextByteLength )
{
- llwarns << "Invalid Linden text length: " << text_len << llendl;
+ LL_WARNS() << "Invalid Linden text length: " << text_len << LL_ENDL;
return FALSE;
}
@@ -2501,21 +2548,21 @@ BOOL LLTextEditor::importBuffer(const char* buffer, S32 length )
char* text = new char[ text_len + 1];
if (text == NULL)
{
- llerrs << "Memory allocation failure." << llendl;
+ LL_ERRS() << "Memory allocation failure." << LL_ENDL;
return FALSE;
}
instream.get(text, text_len + 1, '\0');
text[text_len] = '\0';
if( text_len != (S32)strlen(text) )/* Flawfinder: ignore */
{
- llwarns << llformat("Invalid text length: %d != %d ",strlen(text),text_len) << llendl;/* Flawfinder: ignore */
+ LL_WARNS() << llformat("Invalid text length: %d != %d ",strlen(text),text_len) << LL_ENDL;/* Flawfinder: ignore */
success = FALSE;
}
instream.getline(tbuf, MAX_STRING);
if( success && (0 != sscanf(tbuf, "}")) )
{
- llwarns << "Invalid Linden text file format: missing terminal }" << llendl;
+ LL_WARNS() << "Invalid Linden text file format: missing terminal }" << LL_ENDL;
success = FALSE;
}
@@ -2574,11 +2621,23 @@ BOOL LLTextEditor::hasPreeditString() const
void LLTextEditor::resetPreedit()
{
+ if (hasSelection())
+ {
+ if (hasPreeditString())
+ {
+ LL_WARNS() << "Preedit and selection!" << LL_ENDL;
+ deselect();
+ }
+ else
+ {
+ deleteSelection(TRUE);
+ }
+ }
if (hasPreeditString())
{
if (hasSelection())
{
- llwarns << "Preedit and selection!" << llendl;
+ LL_WARNS() << "Preedit and selection!" << LL_ENDL;
deselect();
}
@@ -2629,7 +2688,10 @@ void LLTextEditor::updatePreedit(const LLWString &preedit_string,
{
mPreeditOverwrittenWString.clear();
}
- insertStringNoUndo(insert_preedit_at, mPreeditWString);
+
+ segment_vec_t segments;
+ //pass empty segments to let "insertStringNoUndo" make new LLNormalTextSegment and insert it, if needed.
+ insertStringNoUndo(insert_preedit_at, mPreeditWString, &segments);
mPreeditStandouts = preedit_standouts;
@@ -2693,11 +2755,11 @@ BOOL LLTextEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect
const LLWString textString(getWText());
const llwchar * const text = textString.c_str();
- const S32 line_height = llround(mDefaultFont->getLineHeight());
+ const S32 line_height = mFont->getLineHeight();
if (coord)
{
- const S32 query_x = mVisibleTextRect.mLeft + mDefaultFont->getWidth(text, current_line_start, query - current_line_start);
+ const S32 query_x = mVisibleTextRect.mLeft + mFont->getWidth(text, current_line_start, query - current_line_start);
const S32 query_y = mVisibleTextRect.mTop - (current_line - first_visible_line) * line_height - line_height / 2;
S32 query_screen_x, query_screen_y;
localPointToScreen(query_x, query_y, &query_screen_x, &query_screen_y);
@@ -2709,17 +2771,17 @@ BOOL LLTextEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect
S32 preedit_left = mVisibleTextRect.mLeft;
if (preedit_left_position > current_line_start)
{
- preedit_left += mDefaultFont->getWidth(text, current_line_start, preedit_left_position - current_line_start);
+ preedit_left += mFont->getWidth(text, current_line_start, preedit_left_position - current_line_start);
}
S32 preedit_right = mVisibleTextRect.mLeft;
if (preedit_right_position < current_line_end)
{
- preedit_right += mDefaultFont->getWidth(text, current_line_start, preedit_right_position - current_line_start);
+ preedit_right += mFont->getWidth(text, current_line_start, preedit_right_position - current_line_start);
}
else
{
- preedit_right += mDefaultFont->getWidth(text, current_line_start, current_line_end - current_line_start);
+ preedit_right += mFont->getWidth(text, current_line_start, current_line_end - current_line_start);
}
const S32 preedit_top = mVisibleTextRect.mTop - (current_line - first_visible_line) * line_height;
@@ -2768,7 +2830,7 @@ void LLTextEditor::markAsPreedit(S32 position, S32 length)
setCursorPos(position);
if (hasPreeditString())
{
- llwarns << "markAsPreedit invoked when hasPreeditString is true." << llendl;
+ LL_WARNS() << "markAsPreedit invoked when hasPreeditString is true." << LL_ENDL;
}
mPreeditWString = LLWString( getWText(), position, length );
if (length > 0)
@@ -2796,7 +2858,7 @@ void LLTextEditor::markAsPreedit(S32 position, S32 length)
S32 LLTextEditor::getPreeditFontSize() const
{
- return llround(mDefaultFont->getLineHeight() * LLUI::sGLScaleFactor.mV[VY]);
+ return ll_round((F32)mFont->getLineHeight() * LLUI::getScaleFactor().mV[VY]);
}
BOOL LLTextEditor::isDirty() const
@@ -2824,6 +2886,9 @@ void LLTextEditor::setKeystrokeCallback(const keystroke_signal_t::slot_type& cal
void LLTextEditor::onKeyStroke()
{
mKeystrokeSignal(this);
+
+ mSpellCheckStart = mSpellCheckEnd = -1;
+ mSpellCheckTimer.setTimerExpirySec(SPELLCHECK_DELAY);
}
//virtual
@@ -2832,3 +2897,13 @@ void LLTextEditor::clear()
getViewModel()->setDisplay(LLWStringUtil::null);
clearSegments();
}
+
+bool LLTextEditor::canLoadOrSaveToFile()
+{
+ return !mReadOnly;
+}
+
+S32 LLTextEditor::spacesPerTab()
+{
+ return SPACES_PER_TAB;
+}
diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h
index 9e4b95003b..f6bdf917b4 100644..100755
--- a/indra/llui/lltexteditor.h
+++ b/indra/llui/lltexteditor.h
@@ -1,25 +1,25 @@
-/**
+/**
* @file lltexteditor.h
* @brief LLTextEditor base class
*
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
- *
+ *
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
- *
+ *
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
- *
+ *
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
+ *
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -30,12 +30,9 @@
#define LL_LLTEXTEDITOR_H
#include "llrect.h"
-#include "llkeywords.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"
@@ -45,7 +42,6 @@
class LLFontGL;
class LLScrollbar;
-class LLKeywordToken;
class TextCmd;
class LLUICtrlFactory;
class LLScrollContainer;
@@ -62,9 +58,10 @@ public:
Optional<bool> embedded_items,
ignore_tab,
- show_line_numbers,
commit_on_focus_lost,
- show_context_menu;
+ show_context_menu,
+ enable_tooltip_paste,
+ auto_indent;
//colors
Optional<LLUIColor> default_color;
@@ -92,6 +89,8 @@ public:
void setParseHighlights(BOOL parsing) {mParseHighlights=parsing;}
+ static S32 spacesPerTab();
+
// mousehandler overrides
virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
@@ -129,7 +128,7 @@ public:
virtual BOOL canCopy() const;
virtual void paste();
virtual BOOL canPaste() const;
-
+
virtual void updatePrimary();
virtual void copyPrimary();
virtual void pastePrimary();
@@ -140,10 +139,14 @@ public:
virtual void selectAll();
virtual BOOL canSelectAll() const;
+ void selectByCursorPosition(S32 prev_cursor_pos, S32 next_cursor_pos);
+
+ virtual bool canLoadOrSaveToFile();
+
void selectNext(const std::string& search_text_in, BOOL case_insensitive, BOOL wrap = TRUE);
BOOL replaceText(const std::string& search_text, const std::string& replace_text, BOOL case_insensitive, BOOL wrap = TRUE);
void replaceTextAll(const std::string& search_text, const std::string& replace_text, BOOL case_insensitive);
-
+
// Undo/redo stack
void blockUndo();
@@ -152,12 +155,18 @@ public:
BOOL isPristine() const;
BOOL allowsEmbeddedItems() const { return mAllowEmbeddedItems; }
+ // Autoreplace (formerly part of LLLineEditor)
+ typedef boost::function<void(S32&, S32&, LLWString&, S32&, const LLWString&)> autoreplace_callback_t;
+ autoreplace_callback_t mAutoreplaceCallback;
+ void setAutoreplaceCallback(autoreplace_callback_t cb) { mAutoreplaceCallback = cb; }
+
//
// Text manipulation
//
// inserts text at cursor
void insertText(const std::string &text);
+ void insertText(LLWString &text);
void appendWidget(const LLInlineViewSegment::Params& params, const std::string& text, bool allow_undo);
// Non-undoable
@@ -174,13 +183,6 @@ public:
void getCurrentLineAndColumn( S32* line, S32* col, BOOL include_wordwrap );
- void loadKeywords(const std::string& filename,
- const std::vector<std::string>& funcs,
- const std::vector<std::string>& tooltips,
- const LLColor3& func_color);
- LLKeywords::keyword_iterator_t keywordsBegin() { return mKeywords.begin(); }
- LLKeywords::keyword_iterator_t keywordsEnd() { return mKeywords.end(); }
-
// Hacky methods to make it into a word-wrapping, potentially scrolling,
// read-only text box.
void setCommitOnFocusLost(BOOL b) { mCommitOnFocusLost = b; }
@@ -192,25 +194,27 @@ public:
const LLUUID& getSourceID() const { return mSourceID; }
const LLTextSegmentPtr getPreviousSegment() const;
- void getSelectedSegments(segment_vec_t& segments) const;
+ void getSelectedSegments(segment_vec_t& segments) const;
void setShowContextMenu(bool show) { mShowContextMenu = show; }
bool getShowContextMenu() const { return mShowContextMenu; }
+ void setPassDelete(BOOL b) { mPassDelete = b; }
+
protected:
void showContextMenu(S32 x, S32 y);
void drawPreeditMarker();
void assignEmbedded(const std::string &s);
-
+
void removeCharOrTab();
void indentSelectedLines( S32 spaces );
S32 indentLine( S32 pos, S32 spaces );
void unindentLineBeforeCloseBrace();
+ virtual BOOL handleSpecialKey(const KEY key, const MASK mask);
BOOL handleNavigationKey(const KEY key, const MASK mask);
- BOOL handleSpecialKey(const KEY key, const MASK mask);
BOOL handleSelectionKey(const KEY key, const MASK mask);
BOOL handleControlKey(const KEY key, const MASK mask);
@@ -221,12 +225,12 @@ protected:
S32 nextWordPos(S32 cursorPos) const;
void autoIndent();
-
+
void findEmbeddedItemSegments(S32 start, S32 end);
void getSegmentsInRange(segment_vec_t& segments, S32 start, S32 end, bool include_partial) const;
virtual llwchar pasteEmbeddedItem(llwchar ext_char) { return ext_char; }
-
+
// Here's the method that takes and applies text commands.
S32 execute(TextCmd* cmd);
@@ -234,13 +238,14 @@ protected:
// Undoable operations
void addChar(llwchar c); // at mCursorPos
S32 addChar(S32 pos, llwchar wc);
- void addLineBreakChar();
+ void addLineBreakChar(BOOL group_together = FALSE);
S32 overwriteChar(S32 pos, llwchar wc);
void removeChar();
S32 removeChar(S32 pos);
S32 insert(S32 pos, const LLWString &wstr, bool group_with_next_op, LLTextSegmentPtr segment);
S32 remove(S32 pos, S32 length, bool group_with_next_op);
-
+
+ void focusLostHelper();
void updateAllowingLanguageInput();
BOOL hasPreeditString() const;
@@ -257,14 +262,14 @@ protected:
//
// Protected data
//
- // Probably deserves serious thought to hiding as many of these
+ // Probably deserves serious thought to hiding as many of these
// as possible behind protected accessor methods.
//
// Use these to determine if a click on an embedded item is a drag or not.
S32 mMouseDownX;
S32 mMouseDownY;
-
+
LLWString mPreeditWString;
LLWString mPreeditOverwrittenWString;
std::vector<S32> mPreeditPositions;
@@ -273,34 +278,29 @@ protected:
protected:
LLUIColor mDefaultColor;
- BOOL mShowLineNumbers;
+ bool mAutoIndent;
+ bool mParseOnTheFly;
- /*virtual*/ void updateSegments();
void updateLinkSegments();
+ void keepSelectionOnReturn(bool keep) { mKeepSelectionOnReturn = keep; }
+ class LLViewBorder* mBorder;
private:
//
// Methods
//
void pasteHelper(bool is_primary);
-
- void drawLineNumbers();
+ void cleanStringForPaste(LLWString & clean_string);
+ void pasteTextWithLinebreaks(LLWString & clean_string);
void onKeyStroke();
- //
- // Data
- //
- LLKeywords mKeywords;
-
// Concrete TextCmd sub-classes used by the LLTextEditor base class
class TextCmdInsert;
class TextCmdAddChar;
class TextCmdOverwriteChar;
class TextCmdRemove;
- class LLViewBorder* mBorder;
-
BOOL mBaseDocIsPristine;
TextCmd* mPristineCmd;
@@ -315,7 +315,9 @@ private:
BOOL mAllowEmbeddedItems;
bool mShowContextMenu;
- bool mParseOnTheFly;
+ bool mEnableTooltipPaste;
+ bool mPassDelete;
+ bool mKeepSelectionOnReturn; // disabling of removing selected text after pressing of Enter
LLUUID mSourceID;
@@ -333,4 +335,4 @@ extern template class LLTextEditor* LLView::getChild<class LLTextEditor>(
const std::string& name, BOOL recurse) const;
#endif
-#endif // LL_TEXTEDITOR_
+#endif // LL_TEXTEDITOR_H
diff --git a/indra/llui/lltextparser.cpp b/indra/llui/lltextparser.cpp
index a4fe4f6ca8..0b36241da0 100644..100755
--- a/indra/llui/lltextparser.cpp
+++ b/indra/llui/lltextparser.cpp
@@ -46,8 +46,6 @@ LLTextParser::LLTextParser()
{}
-// Moved triggerAlerts() to llfloaterchat.cpp to break llui/llaudio library dependency.
-
S32 LLTextParser::findPattern(const std::string &text, LLSD highlight)
{
if (!highlight.has("pattern")) return -1;
@@ -230,7 +228,7 @@ bool LLTextParser::saveToDisk(LLSD highlights)
std::string filename=getFileName();
if (filename.empty())
{
- llwarns << "LLTextParser::saveToDisk() no valid user directory." << llendl;
+ LL_WARNS() << "LLTextParser::saveToDisk() no valid user directory." << LL_ENDL;
return FALSE;
}
llofstream file;
diff --git a/indra/llui/lltextparser.h b/indra/llui/lltextparser.h
index 400aeeb8be..400aeeb8be 100644..100755
--- a/indra/llui/lltextparser.h
+++ b/indra/llui/lltextparser.h
diff --git a/indra/llui/lltextutil.cpp b/indra/llui/lltextutil.cpp
index 4df2c3363f..fff04b34f2 100644..100755
--- a/indra/llui/lltextutil.cpp
+++ b/indra/llui/lltextutil.cpp
@@ -72,7 +72,7 @@ const std::string& LLTextUtil::formatPhoneNumber(const std::string& phone_str)
return formatted_phone_str;
}
-bool LLTextUtil::processUrlMatch(LLUrlMatch* match,LLTextBase* text_base)
+bool LLTextUtil::processUrlMatch(LLUrlMatch* match,LLTextBase* text_base, bool is_content_trusted)
{
if (match == 0 || text_base == 0)
return false;
@@ -85,7 +85,7 @@ bool LLTextUtil::processUrlMatch(LLUrlMatch* match,LLTextBase* text_base)
}
// output an optional icon before the Url
- if (!match->getIcon().empty() )
+ if (is_content_trusted && !match->getIcon().empty() )
{
LLUIImagePtr image = LLUI::getUIImage(match->getIcon());
if (image)
diff --git a/indra/llui/lltextutil.h b/indra/llui/lltextutil.h
index bf7dbb58ce..1be81ffd62 100644..100755
--- a/indra/llui/lltextutil.h
+++ b/indra/llui/lltextutil.h
@@ -64,7 +64,15 @@ namespace LLTextUtil
*/
const std::string& formatPhoneNumber(const std::string& phone_str);
- bool processUrlMatch(LLUrlMatch* match,LLTextBase* text_base);
+ /**
+ * Adds icon before url if need.
+ *
+ * @param[in] match an object with results of matching
+ * @param[in] text_base pointer to UI text object
+ * @param[in] is_content_trusted true if context is trusted
+ * @return reference to string with formatted phone number
+ */
+ bool processUrlMatch(LLUrlMatch* match, LLTextBase* text_base, bool is_content_trusted);
class TextHelpers
{
diff --git a/indra/llui/lltextvalidate.cpp b/indra/llui/lltextvalidate.cpp
index 4b9faa0560..324ceb7fba 100644..100755
--- a/indra/llui/lltextvalidate.cpp
+++ b/indra/llui/lltextvalidate.cpp
@@ -188,6 +188,39 @@ namespace LLTextValidate
return success;
}
+ bool validateNonNegativeS32NoSpace(const LLWString &str)
+ {
+ LLLocale locale(LLLocale::USER_LOCALE);
+
+ LLWString test_str = str;
+ S32 len = test_str.length();
+ bool success = TRUE;
+ if(0 < len)
+ {
+ if('-' == test_str[0])
+ {
+ success = FALSE;
+ }
+ S32 i = 0;
+ while(success && (i < len))
+ {
+ if(!LLStringOps::isDigit(test_str[i]) || LLStringOps::isSpace(test_str[i++]))
+ {
+ success = FALSE;
+ }
+ }
+ }
+ if (success)
+ {
+ S32 val = strtol(wstring_to_utf8str(test_str).c_str(), NULL, 10);
+ if (val < 0)
+ {
+ success = FALSE;
+ }
+ }
+ return success;
+ }
+
bool validateAlphaNum(const LLWString &str)
{
LLLocale locale(LLLocale::USER_LOCALE);
@@ -303,7 +336,7 @@ namespace LLTextValidate
S32 len = str.length();
while(len--)
{
- if (str[len] < 0x20 && str[len] != 0xA || str[len] > 0x7f)
+ if ((str[len] < 0x20 && str[len] != 0xA) || str[len] > 0x7f)
{
rv = FALSE;
break;
diff --git a/indra/llui/lltextvalidate.h b/indra/llui/lltextvalidate.h
index 84644be30c..5c830d7db3 100644..100755
--- a/indra/llui/lltextvalidate.h
+++ b/indra/llui/lltextvalidate.h
@@ -46,6 +46,7 @@ namespace LLTextValidate
bool validateInt(const LLWString &str );
bool validatePositiveS32(const LLWString &str);
bool validateNonNegativeS32(const LLWString &str);
+ bool validateNonNegativeS32NoSpace(const LLWString &str);
bool validateAlphaNum(const LLWString &str );
bool validateAlphaNumSpace(const LLWString &str );
bool validateASCIIPrintableNoPipe(const LLWString &str);
diff --git a/indra/llui/lltimectrl.cpp b/indra/llui/lltimectrl.cpp
new file mode 100755
index 0000000000..516057f8fd
--- /dev/null
+++ b/indra/llui/lltimectrl.cpp
@@ -0,0 +1,433 @@
+/**
+ * @file lltimectrl.cpp
+ * @brief LLTimeCtrl base class
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2011, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "lltimectrl.h"
+
+#include "llui.h"
+#include "lluiconstants.h"
+
+#include "llbutton.h"
+#include "llfontgl.h"
+#include "lllineeditor.h"
+#include "llkeyboard.h"
+#include "llstring.h"
+#include "lltextbox.h"
+#include "lluictrlfactory.h"
+
+static LLDefaultChildRegistry::Register<LLTimeCtrl> time_r("time");
+
+const U32 AMPM_LEN = 3;
+const U32 MINUTES_MIN = 0;
+const U32 MINUTES_MAX = 59;
+const U32 HOURS_MIN = 1;
+const U32 HOURS_MAX = 12;
+const U32 MINUTES_PER_HOUR = 60;
+const U32 MINUTES_PER_DAY = 24 * MINUTES_PER_HOUR;
+
+
+LLTimeCtrl::Params::Params()
+: label_width("label_width"),
+ snap_to("snap_to"),
+ allow_text_entry("allow_text_entry", true),
+ text_enabled_color("text_enabled_color"),
+ text_disabled_color("text_disabled_color"),
+ up_button("up_button"),
+ down_button("down_button")
+{}
+
+LLTimeCtrl::LLTimeCtrl(const LLTimeCtrl::Params& p)
+: LLUICtrl(p),
+ mLabelBox(NULL),
+ mTextEnabledColor(p.text_enabled_color()),
+ mTextDisabledColor(p.text_disabled_color()),
+ mTime(0),
+ mSnapToMin(5)
+{
+ static LLUICachedControl<S32> spinctrl_spacing ("UISpinctrlSpacing", 0);
+ static LLUICachedControl<S32> spinctrl_btn_width ("UISpinctrlBtnWidth", 0);
+ static LLUICachedControl<S32> spinctrl_btn_height ("UISpinctrlBtnHeight", 0);
+ S32 centered_top = getRect().getHeight();
+ S32 centered_bottom = getRect().getHeight() - 2 * spinctrl_btn_height;
+ S32 label_width = llclamp(p.label_width(), 0, llmax(0, getRect().getWidth() - 40));
+ S32 editor_left = label_width + spinctrl_spacing;
+
+ //================= Label =================//
+ if( !p.label().empty() )
+ {
+ LLRect label_rect( 0, centered_top, label_width, centered_bottom );
+ LLTextBox::Params params;
+ params.name("TimeCtrl Label");
+ params.rect(label_rect);
+ params.initial_value(p.label());
+ if (p.font.isProvided())
+ {
+ params.font(p.font);
+ }
+ mLabelBox = LLUICtrlFactory::create<LLTextBox> (params);
+ addChild(mLabelBox);
+
+ editor_left = label_rect.mRight + spinctrl_spacing;
+ }
+
+ S32 editor_right = getRect().getWidth() - spinctrl_btn_width - spinctrl_spacing;
+
+ //================= Editor ================//
+ LLRect editor_rect( editor_left, centered_top, editor_right, centered_bottom );
+ LLLineEditor::Params params;
+ params.name("SpinCtrl Editor");
+ params.rect(editor_rect);
+ if (p.font.isProvided())
+ {
+ params.font(p.font);
+ }
+
+ params.follows.flags(FOLLOWS_LEFT | FOLLOWS_BOTTOM);
+ params.max_length.chars(8);
+ params.keystroke_callback(boost::bind(&LLTimeCtrl::onTextEntry, this, _1));
+ mEditor = LLUICtrlFactory::create<LLLineEditor> (params);
+ mEditor->setPrevalidateInput(LLTextValidate::validateNonNegativeS32NoSpace);
+ mEditor->setPrevalidate(boost::bind(&LLTimeCtrl::isTimeStringValid, this, _1));
+ mEditor->setText(LLStringExplicit("12:00 AM"));
+ addChild(mEditor);
+
+ //================= Spin Buttons ==========//
+ LLButton::Params up_button_params(p.up_button);
+ up_button_params.rect = LLRect(editor_right + 1, getRect().getHeight(), editor_right + spinctrl_btn_width, getRect().getHeight() - spinctrl_btn_height);
+
+ up_button_params.click_callback.function(boost::bind(&LLTimeCtrl::onUpBtn, this));
+ up_button_params.mouse_held_callback.function(boost::bind(&LLTimeCtrl::onUpBtn, this));
+ mUpBtn = LLUICtrlFactory::create<LLButton>(up_button_params);
+ addChild(mUpBtn);
+
+ LLButton::Params down_button_params(p.down_button);
+ down_button_params.rect = LLRect(editor_right + 1, getRect().getHeight() - spinctrl_btn_height, editor_right + spinctrl_btn_width, getRect().getHeight() - 2 * spinctrl_btn_height);
+ down_button_params.click_callback.function(boost::bind(&LLTimeCtrl::onDownBtn, this));
+ down_button_params.mouse_held_callback.function(boost::bind(&LLTimeCtrl::onDownBtn, this));
+ mDownBtn = LLUICtrlFactory::create<LLButton>(down_button_params);
+ addChild(mDownBtn);
+
+ setUseBoundingRect( TRUE );
+}
+
+F32 LLTimeCtrl::getTime24() const
+{
+ // 0.0 - 23.99;
+ return mTime / 60.0f;
+}
+
+U32 LLTimeCtrl::getHours24() const
+{
+ return (U32) getTime24();
+}
+
+U32 LLTimeCtrl::getMinutes() const
+{
+ return mTime % MINUTES_PER_HOUR;
+}
+
+void LLTimeCtrl::setTime24(F32 time)
+{
+ time = llclamp(time, 0.0f, 23.99f); // fix out of range values
+ mTime = ll_round(time * MINUTES_PER_HOUR); // fixes values like 4.99999
+
+ updateText();
+}
+
+BOOL LLTimeCtrl::handleKeyHere(KEY key, MASK mask)
+{
+ if (mEditor->hasFocus())
+ {
+ if(key == KEY_UP)
+ {
+ onUpBtn();
+ return TRUE;
+ }
+ if(key == KEY_DOWN)
+ {
+ onDownBtn();
+ return TRUE;
+ }
+ if (key == KEY_RETURN)
+ {
+ onCommit();
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+void LLTimeCtrl::onUpBtn()
+{
+ switch(getEditingPart())
+ {
+ case HOURS:
+ increaseHours();
+ break;
+ case MINUTES:
+ increaseMinutes();
+ break;
+ case DAYPART:
+ switchDayPeriod();
+ break;
+ default:
+ break;
+ }
+
+ updateText();
+ onCommit();
+}
+
+void LLTimeCtrl::onDownBtn()
+{
+ switch(getEditingPart())
+ {
+ case HOURS:
+ decreaseHours();
+ break;
+ case MINUTES:
+ decreaseMinutes();
+ break;
+ case DAYPART:
+ switchDayPeriod();
+ break;
+ default:
+ break;
+ }
+
+ updateText();
+ onCommit();
+}
+
+void LLTimeCtrl::onFocusLost()
+{
+ updateText();
+ onCommit();
+ LLUICtrl::onFocusLost();
+}
+
+void LLTimeCtrl::onTextEntry(LLLineEditor* line_editor)
+{
+ std::string time_str = line_editor->getText();
+ U32 h12 = parseHours(getHoursString(time_str));
+ U32 m = parseMinutes(getMinutesString(time_str));
+ bool pm = parseAMPM(getAMPMString(time_str));
+
+ if (h12 == 12)
+ {
+ h12 = 0;
+ }
+
+ U32 h24 = pm ? h12 + 12 : h12;
+
+ mTime = h24 * MINUTES_PER_HOUR + m;
+}
+
+bool LLTimeCtrl::isTimeStringValid(const LLWString &wstr)
+{
+ std::string str = wstring_to_utf8str(wstr);
+
+ return isHoursStringValid(getHoursString(str)) &&
+ isMinutesStringValid(getMinutesString(str)) &&
+ isPMAMStringValid(getAMPMString(str));
+}
+
+void LLTimeCtrl::increaseMinutes()
+{
+ mTime = (mTime + mSnapToMin) % MINUTES_PER_DAY - (mTime % mSnapToMin);
+}
+
+void LLTimeCtrl::increaseHours()
+{
+ mTime = (mTime + MINUTES_PER_HOUR) % MINUTES_PER_DAY;
+}
+
+void LLTimeCtrl::decreaseMinutes()
+{
+ if (mTime < mSnapToMin)
+ {
+ mTime = MINUTES_PER_DAY - mTime;
+ }
+
+ mTime -= (mTime % mSnapToMin) ? mTime % mSnapToMin : mSnapToMin;
+}
+
+void LLTimeCtrl::decreaseHours()
+{
+ if (mTime < MINUTES_PER_HOUR)
+ {
+ mTime = 23 * MINUTES_PER_HOUR + mTime;
+ }
+ else
+ {
+ mTime -= MINUTES_PER_HOUR;
+ }
+}
+
+bool LLTimeCtrl::isPM() const
+{
+ return mTime >= (MINUTES_PER_DAY / 2);
+}
+
+void LLTimeCtrl::switchDayPeriod()
+{
+ if (isPM())
+ {
+ mTime -= MINUTES_PER_DAY / 2;
+ }
+ else
+ {
+ mTime += MINUTES_PER_DAY / 2;
+ }
+}
+
+void LLTimeCtrl::updateText()
+{
+ U32 h24 = getHours24();
+ U32 m = getMinutes();
+ U32 h12 = h24 > 12 ? h24 - 12 : h24;
+
+ if (h12 == 0)
+ h12 = 12;
+
+ mEditor->setText(llformat("%d:%02d %s", h12, m, isPM() ? "PM":"AM"));
+}
+
+LLTimeCtrl::EEditingPart LLTimeCtrl::getEditingPart()
+{
+ S32 cur_pos = mEditor->getCursor();
+ std::string time_str = mEditor->getText();
+
+ S32 colon_index = time_str.find_first_of(':');
+
+ if (cur_pos <= colon_index)
+ {
+ return HOURS;
+ }
+ else if (cur_pos > colon_index && cur_pos <= (S32)(time_str.length() - AMPM_LEN))
+ {
+ return MINUTES;
+ }
+ else if (cur_pos > (S32)(time_str.length() - AMPM_LEN))
+ {
+ return DAYPART;
+ }
+
+ return NONE;
+}
+
+// static
+std::string LLTimeCtrl::getHoursString(const std::string& str)
+{
+ size_t colon_index = str.find_first_of(':');
+ std::string hours_str = str.substr(0, colon_index);
+
+ return hours_str;
+}
+
+// static
+std::string LLTimeCtrl::getMinutesString(const std::string& str)
+{
+ size_t colon_index = str.find_first_of(':');
+ ++colon_index;
+
+ int minutes_len = str.length() - colon_index - AMPM_LEN;
+ std::string minutes_str = str.substr(colon_index, minutes_len);
+
+ return minutes_str;
+}
+
+// static
+std::string LLTimeCtrl::getAMPMString(const std::string& str)
+{
+ return str.substr(str.size() - 2, 2); // returns last two characters
+}
+
+// static
+bool LLTimeCtrl::isHoursStringValid(const std::string& str)
+{
+ U32 hours;
+ if ((!LLStringUtil::convertToU32(str, hours) || (hours <= HOURS_MAX)) && str.length() < 3)
+ return true;
+
+ return false;
+}
+
+// static
+bool LLTimeCtrl::isMinutesStringValid(const std::string& str)
+{
+ U32 minutes;
+ if (!LLStringUtil::convertToU32(str, minutes) || ((minutes <= MINUTES_MAX) && str.length() < 3))
+ return true;
+
+ return false;
+}
+
+// static
+bool LLTimeCtrl::isPMAMStringValid(const std::string& str)
+{
+ S32 len = str.length();
+
+ bool valid = (str[--len] == 'M') && (str[--len] == 'P' || str[len] == 'A');
+
+ return valid;
+}
+
+// static
+U32 LLTimeCtrl::parseHours(const std::string& str)
+{
+ U32 hours;
+ if (LLStringUtil::convertToU32(str, hours) && (hours >= HOURS_MIN) && (hours <= HOURS_MAX))
+ {
+ return hours;
+ }
+ else
+ {
+ return HOURS_MIN;
+ }
+}
+
+// static
+U32 LLTimeCtrl::parseMinutes(const std::string& str)
+{
+ U32 minutes;
+ // not sure of this fix - clang doesnt like compare minutes U32 to >= MINUTES_MIN (0) but MINUTES_MIN can change
+ if (LLStringUtil::convertToU32(str, minutes) && ((S32)minutes >= MINUTES_MIN) && ((S32)minutes <= MINUTES_MAX))
+ {
+ return minutes;
+ }
+ else
+ {
+ return MINUTES_MIN;
+ }
+}
+
+// static
+bool LLTimeCtrl::parseAMPM(const std::string& str)
+{
+ return str == "PM";
+}
diff --git a/indra/llui/lltimectrl.h b/indra/llui/lltimectrl.h
new file mode 100755
index 0000000000..b5f268c76a
--- /dev/null
+++ b/indra/llui/lltimectrl.h
@@ -0,0 +1,131 @@
+/**
+ * @file lltimectrl.h
+ * @brief Time control
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2011, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LLTIMECTRL_H_
+#define LLTIMECTRL_H_
+
+#include "stdtypes.h"
+#include "llbutton.h"
+#include "v4color.h"
+#include "llrect.h"
+
+class LLLineEditor;
+
+class LLTimeCtrl
+: public LLUICtrl
+{
+ LOG_CLASS(LLTimeCtrl);
+public:
+ struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
+ {
+ Optional<S32> label_width;
+ Optional<S32> snap_to;
+ Optional<bool> allow_text_entry;
+
+ Optional<LLUIColor> text_enabled_color;
+ Optional<LLUIColor> text_disabled_color;
+
+ Optional<LLButton::Params> up_button;
+ Optional<LLButton::Params> down_button;
+
+ Params();
+ };
+
+ F32 getTime24() const; // 0.0 - 24.0
+ U32 getHours24() const; // 0 - 23
+ U32 getMinutes() const; // 0 - 59
+
+ void setTime24(F32 time); // 0.0 - 23.98(3)
+
+protected:
+ LLTimeCtrl(const Params&);
+ friend class LLUICtrlFactory;
+
+private:
+
+ enum EDayPeriod
+ {
+ AM,
+ PM
+ };
+
+ enum EEditingPart
+ {
+ HOURS,
+ MINUTES,
+ DAYPART,
+ NONE
+ };
+
+ virtual void onFocusLost();
+ virtual BOOL handleKeyHere(KEY key, MASK mask);
+
+ void onUpBtn();
+ void onDownBtn();
+ void onTextEntry(LLLineEditor* line_editor);
+
+ bool isTimeStringValid(const LLWString& wstr);
+
+ void increaseMinutes();
+ void increaseHours();
+
+ void decreaseMinutes();
+ void decreaseHours();
+
+ bool isPM() const;
+ void switchDayPeriod();
+
+ void updateText();
+
+ EEditingPart getEditingPart();
+
+ static std::string getHoursString(const std::string& str);
+ static std::string getMinutesString(const std::string& str);
+ static std::string getAMPMString(const std::string& str);
+
+ static bool isHoursStringValid(const std::string& str);
+ static bool isMinutesStringValid(const std::string& str);
+ static bool isPMAMStringValid(const std::string& str);
+
+ static U32 parseHours(const std::string& str);
+ static U32 parseMinutes(const std::string& str);
+ static bool parseAMPM(const std::string& str);
+
+ class LLTextBox* mLabelBox;
+
+ class LLLineEditor* mEditor;
+ LLUIColor mTextEnabledColor;
+ LLUIColor mTextDisabledColor;
+
+ class LLButton* mUpBtn;
+ class LLButton* mDownBtn;
+
+ U32 mTime; // minutes since midnight: 0 - 1439
+ U32 mSnapToMin; // interval in minutes to snap to
+
+ BOOL mAllowEdit;
+};
+#endif /* LLTIMECTRL_H_ */
diff --git a/indra/llui/lltoggleablemenu.cpp b/indra/llui/lltoggleablemenu.cpp
index d29260750f..ccb92ffbb2 100644..100755
--- a/indra/llui/lltoggleablemenu.cpp
+++ b/indra/llui/lltoggleablemenu.cpp
@@ -52,12 +52,14 @@ boost::signals2::connection LLToggleableMenu::setVisibilityChangeCallback(const
}
// virtual
-void LLToggleableMenu::handleVisibilityChange (BOOL curVisibilityIn)
+void LLToggleableMenu::onVisibilityChange (BOOL curVisibilityIn)
{
S32 x,y;
LLUI::getMousePositionLocal(LLUI::getRootView(), &x, &y);
- if (!curVisibilityIn && mButtonRect.pointInRect(x, y))
+ // STORM-1879: also check MouseCapture to see if the button was really
+ // clicked (otherwise the VisibilityChange was triggered via keyboard shortcut)
+ if (!curVisibilityIn && mButtonRect.pointInRect(x, y) && gFocusMgr.getMouseCapture())
{
mClosedByButtonClick = true;
}
@@ -99,3 +101,8 @@ bool LLToggleableMenu::toggleVisibility()
return true;
}
+
+bool LLToggleableMenu::addChild(LLView* view, S32 tab_group)
+{
+ return addContextChild(view, tab_group);
+}
diff --git a/indra/llui/lltoggleablemenu.h b/indra/llui/lltoggleablemenu.h
index 2094bd776f..55a6483021 100644..100755
--- a/indra/llui/lltoggleablemenu.h
+++ b/indra/llui/lltoggleablemenu.h
@@ -45,7 +45,9 @@ public:
boost::signals2::connection setVisibilityChangeCallback( const commit_signal_t::slot_type& cb );
- virtual void handleVisibilityChange (BOOL curVisibilityIn);
+ virtual void onVisibilityChange (BOOL curVisibilityIn);
+
+ virtual bool addChild (LLView* view, S32 tab_group = 0);
const LLRect& getButtonRect() const { return mButtonRect; }
@@ -58,6 +60,8 @@ public:
// its visibility off.
bool toggleVisibility();
+ LLHandle<LLToggleableMenu> getHandle() { return getDerivedHandle<LLToggleableMenu>(); }
+
protected:
bool mClosedByButtonClick;
LLRect mButtonRect;
diff --git a/indra/llui/lltoolbar.cpp b/indra/llui/lltoolbar.cpp
new file mode 100755
index 0000000000..abc2b6e9ca
--- /dev/null
+++ b/indra/llui/lltoolbar.cpp
@@ -0,0 +1,1259 @@
+/**
+ * @file lltoolbar.cpp
+ * @author Richard Nelson
+ * @brief User customizable toolbar class
+ *
+ * $LicenseInfo:firstyear=2011&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2011, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include <boost/foreach.hpp>
+#include "lltoolbar.h"
+
+#include "llcommandmanager.h"
+#include "llmenugl.h"
+#include "lltrans.h"
+#include "llinventory.h"
+#include "lliconctrl.h"
+
+// uncomment this and remove the one in llui.cpp when there is an external reference to this translation unit
+// thanks, MSVC!
+//static LLDefaultChildRegistry::Register<LLToolBar> r1("toolbar");
+
+namespace LLToolBarEnums
+{
+ LLView::EOrientation getOrientation(SideType sideType)
+ {
+ LLView::EOrientation orientation = LLLayoutStack::HORIZONTAL;
+
+ if ((sideType == SIDE_LEFT) || (sideType == SIDE_RIGHT))
+ {
+ orientation = LLLayoutStack::VERTICAL;
+ }
+
+ return orientation;
+ }
+}
+
+using namespace LLToolBarEnums;
+
+
+namespace LLInitParam
+{
+ void TypeValues<ButtonType>::declareValues()
+ {
+ declare("icons_with_text", BTNTYPE_ICONS_WITH_TEXT);
+ declare("icons_only", BTNTYPE_ICONS_ONLY);
+ }
+
+ void TypeValues<SideType>::declareValues()
+ {
+ declare("bottom", SIDE_BOTTOM);
+ declare("left", SIDE_LEFT);
+ declare("right", SIDE_RIGHT);
+ declare("top", SIDE_TOP);
+ }
+}
+
+LLToolBar::Params::Params()
+: button_display_mode("button_display_mode"),
+ commands("command"),
+ side("side", SIDE_TOP),
+ button_icon("button_icon"),
+ button_icon_and_text("button_icon_and_text"),
+ read_only("read_only", false),
+ wrap("wrap", true),
+ pad_left("pad_left"),
+ pad_top("pad_top"),
+ pad_right("pad_right"),
+ pad_bottom("pad_bottom"),
+ pad_between("pad_between"),
+ min_girth("min_girth"),
+ button_panel("button_panel")
+{}
+
+LLToolBar::LLToolBar(const LLToolBar::Params& p)
+: LLUICtrl(p),
+ mReadOnly(p.read_only),
+ mButtonType(p.button_display_mode),
+ mSideType(p.side),
+ mWrap(p.wrap),
+ mNeedsLayout(false),
+ mModified(false),
+ mButtonPanel(NULL),
+ mCenteringStack(NULL),
+ mPadLeft(p.pad_left),
+ mPadRight(p.pad_right),
+ mPadTop(p.pad_top),
+ mPadBottom(p.pad_bottom),
+ mPadBetween(p.pad_between),
+ mMinGirth(p.min_girth),
+ mPopupMenuHandle(),
+ mRightMouseTargetButton(NULL),
+ mStartDragItemCallback(NULL),
+ mHandleDragItemCallback(NULL),
+ mHandleDropCallback(NULL),
+ mButtonAddSignal(NULL),
+ mButtonEnterSignal(NULL),
+ mButtonLeaveSignal(NULL),
+ mButtonRemoveSignal(NULL),
+ mDragAndDropTarget(false),
+ mCaretIcon(NULL),
+ mCenterPanel(NULL)
+{
+ mButtonParams[LLToolBarEnums::BTNTYPE_ICONS_WITH_TEXT] = p.button_icon_and_text;
+ mButtonParams[LLToolBarEnums::BTNTYPE_ICONS_ONLY] = p.button_icon;
+}
+
+LLToolBar::~LLToolBar()
+{
+ delete mPopupMenuHandle.get();
+ delete mButtonAddSignal;
+ delete mButtonEnterSignal;
+ delete mButtonLeaveSignal;
+ delete mButtonRemoveSignal;
+}
+
+void LLToolBar::createContextMenu()
+{
+ if (!mPopupMenuHandle.get())
+ {
+ // Setup bindings specific to this instance for the context menu options
+
+ LLUICtrl::CommitCallbackRegistry::ScopedRegistrar commit_reg;
+ commit_reg.add("Toolbars.EnableSetting", boost::bind(&LLToolBar::onSettingEnable, this, _2));
+ commit_reg.add("Toolbars.RemoveSelectedCommand", boost::bind(&LLToolBar::onRemoveSelectedCommand, this));
+
+ LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_reg;
+ enable_reg.add("Toolbars.CheckSetting", boost::bind(&LLToolBar::isSettingChecked, this, _2));
+
+ // Create the context menu
+ llassert(LLMenuGL::sMenuContainer != NULL);
+ LLContextMenu* menu = LLUICtrlFactory::instance().createFromFile<LLContextMenu>("menu_toolbars.xml", LLMenuGL::sMenuContainer, LLMenuHolderGL::child_registry_t::instance());
+
+ if (menu)
+ {
+ menu->setBackgroundColor(LLUIColorTable::instance().getColor("MenuPopupBgColor"));
+ mPopupMenuHandle = menu->getHandle();
+ mRemoveButtonHandle = menu->getChild<LLView>("Remove button")->getHandle();
+ }
+ else
+ {
+ LL_WARNS() << "Unable to load toolbars context menu." << LL_ENDL;
+ }
+ }
+
+ if (mRemoveButtonHandle.get())
+ {
+ // Disable/Enable the "Remove button" menu item depending on whether or not a button was clicked
+ mRemoveButtonHandle.get()->setEnabled(mRightMouseTargetButton != NULL);
+ }
+}
+
+void LLToolBar::initFromParams(const LLToolBar::Params& p)
+{
+ // Initialize the base object
+ LLUICtrl::initFromParams(p);
+
+ LLView::EOrientation orientation = getOrientation(p.side);
+
+ LLLayoutStack::Params centering_stack_p;
+ centering_stack_p.name = "centering_stack";
+ centering_stack_p.rect = getLocalRect();
+ centering_stack_p.follows.flags = FOLLOWS_ALL;
+ centering_stack_p.orientation = orientation;
+ centering_stack_p.mouse_opaque = false;
+
+ mCenteringStack = LLUICtrlFactory::create<LLLayoutStack>(centering_stack_p);
+ addChild(mCenteringStack);
+
+ LLLayoutPanel::Params border_panel_p;
+ border_panel_p.name = "border_panel";
+ border_panel_p.rect = getLocalRect();
+ border_panel_p.auto_resize = true;
+ border_panel_p.user_resize = false;
+ border_panel_p.mouse_opaque = false;
+
+ mCenteringStack->addChild(LLUICtrlFactory::create<LLLayoutPanel>(border_panel_p));
+
+ LLLayoutPanel::Params center_panel_p;
+ center_panel_p.name = "center_panel";
+ center_panel_p.rect = getLocalRect();
+ center_panel_p.auto_resize = false;
+ center_panel_p.user_resize = false;
+ center_panel_p.mouse_opaque = false;
+ mCenterPanel = LLUICtrlFactory::create<LLCenterLayoutPanel>(center_panel_p);
+ mCenteringStack->addChild(mCenterPanel);
+
+ LLPanel::Params button_panel_p(p.button_panel);
+ button_panel_p.rect = mCenterPanel->getLocalRect();
+ button_panel_p.follows.flags = FOLLOWS_BOTTOM|FOLLOWS_LEFT;
+ mButtonPanel = LLUICtrlFactory::create<LLPanel>(button_panel_p);
+ mCenterPanel->setButtonPanel(mButtonPanel);
+ mCenterPanel->addChild(mButtonPanel);
+
+ mCenteringStack->addChild(LLUICtrlFactory::create<LLLayoutPanel>(border_panel_p));
+
+ BOOST_FOREACH(LLCommandId id, p.commands)
+ {
+ addCommand(id);
+ }
+
+ mNeedsLayout = true;
+}
+
+bool LLToolBar::addCommand(const LLCommandId& commandId, int rank)
+{
+ LLCommand * command = LLCommandManager::instance().getCommand(commandId);
+ if (!command) return false;
+
+ // Create the button and do the things that don't need ordering
+ LLToolBarButton* button = createButton(commandId);
+ mButtonPanel->addChild(button);
+ mButtonMap.insert(std::make_pair(commandId.uuid(), button));
+
+ // Insert the command and button in the right place in their respective lists
+ if ((rank >= mButtonCommands.size()) || (rank == RANK_NONE))
+ {
+ // In that case, back load
+ mButtonCommands.push_back(command->id());
+ mButtons.push_back(button);
+ }
+ else
+ {
+ // Insert in place: iterate to the right spot...
+ std::list<LLToolBarButton*>::iterator it_button = mButtons.begin();
+ command_id_list_t::iterator it_command = mButtonCommands.begin();
+ while (rank > 0)
+ {
+ ++it_button;
+ ++it_command;
+ rank--;
+ }
+ // ...then insert
+ mButtonCommands.insert(it_command, command->id());
+ mButtons.insert(it_button,button);
+ }
+
+ mNeedsLayout = true;
+
+ updateLayoutAsNeeded();
+
+
+ if (mButtonAddSignal)
+ {
+ (*mButtonAddSignal)(button);
+ }
+
+ return true;
+}
+
+// Remove a command from the list
+// Returns the rank of the command in the original list so that doing addCommand(id,rank) right after
+// a removeCommand(id) would leave the list unchanged.
+// Returns RANK_NONE if the command is not found in the list
+int LLToolBar::removeCommand(const LLCommandId& commandId)
+{
+ if (!hasCommand(commandId)) return RANK_NONE;
+
+ // First erase the map record
+ command_id_map::iterator it = mButtonMap.find(commandId.uuid());
+ mButtonMap.erase(it);
+
+ // Now iterate on the commands and buttons to identify the relevant records
+ int rank = 0;
+ std::list<LLToolBarButton*>::iterator it_button = mButtons.begin();
+ command_id_list_t::iterator it_command = mButtonCommands.begin();
+ while (*it_command != commandId)
+ {
+ ++it_button;
+ ++it_command;
+ ++rank;
+ }
+
+ if (mButtonRemoveSignal)
+ {
+ (*mButtonRemoveSignal)(*it_button);
+ }
+
+ // Delete the button and erase the command and button records
+ delete (*it_button);
+ mButtonCommands.erase(it_command);
+ mButtons.erase(it_button);
+
+ mNeedsLayout = true;
+
+ return rank;
+}
+
+void LLToolBar::clearCommandsList()
+{
+ // Clears the commands list
+ mButtonCommands.clear();
+ // This will clear the buttons
+ createButtons();
+}
+
+bool LLToolBar::hasCommand(const LLCommandId& commandId) const
+{
+ if (commandId != LLCommandId::null)
+ {
+ command_id_map::const_iterator it = mButtonMap.find(commandId.uuid());
+ return (it != mButtonMap.end());
+ }
+
+ return false;
+}
+
+bool LLToolBar::enableCommand(const LLCommandId& commandId, bool enabled)
+{
+ LLButton * command_button = NULL;
+
+ if (commandId != LLCommandId::null)
+ {
+ command_id_map::iterator it = mButtonMap.find(commandId.uuid());
+ if (it != mButtonMap.end())
+ {
+ command_button = it->second;
+ command_button->setEnabled(enabled);
+ }
+ }
+
+ return (command_button != NULL);
+}
+
+bool LLToolBar::stopCommandInProgress(const LLCommandId& commandId)
+{
+ //
+ // Note from Leslie:
+ //
+ // This implementation was largely put in place to handle EXP-1348 which is related to
+ // dragging and dropping the "speak" button. The "speak" button can be in one of two
+ // modes, i.e., either a toggle action or a push-to-talk action. Because of this it
+ // responds to mouse down and mouse up in different ways, based on which behavior the
+ // button is currently set to obey. This was the simplest way of getting the button
+ // to turn off the microphone for both behaviors without risking duplicate state.
+ //
+
+ LLToolBarButton * command_button = NULL;
+
+ if (commandId != LLCommandId::null)
+ {
+ LLCommand* command = LLCommandManager::instance().getCommand(commandId);
+ llassert(command);
+
+ // If this command has an explicit function for execution stop
+ if (command->executeStopFunctionName().length() > 0)
+ {
+ command_id_map::iterator it = mButtonMap.find(commandId.uuid());
+ if (it != mButtonMap.end())
+ {
+ command_button = it->second;
+ llassert(command_button->mIsRunningSignal);
+
+ // Check to see if it is running
+ if ((*command_button->mIsRunningSignal)(command_button, command->isRunningParameters()))
+ {
+ // Trigger an additional button commit, which calls mouse down, mouse up and commit
+ command_button->onCommit();
+ }
+ }
+ }
+ }
+
+ return (command_button != NULL);
+}
+
+bool LLToolBar::flashCommand(const LLCommandId& commandId, bool flash, bool force_flashing/* = false */)
+{
+ LLButton * command_button = NULL;
+
+ if (commandId != LLCommandId::null)
+ {
+ command_id_map::iterator it = mButtonMap.find(commandId.uuid());
+ if (it != mButtonMap.end())
+ {
+ command_button = it->second;
+ command_button->setFlashing((BOOL)(flash),(BOOL)(force_flashing));
+ }
+ }
+
+ return (command_button != NULL);
+}
+
+BOOL LLToolBar::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+ LLRect button_panel_rect;
+ mButtonPanel->localRectToOtherView(mButtonPanel->getLocalRect(), &button_panel_rect, this);
+ BOOL handle_it_here = !mReadOnly && button_panel_rect.pointInRect(x, y);
+
+ if (handle_it_here)
+ {
+ // Determine which button the mouse was over during the click in case the context menu action
+ // is intended to affect the button.
+ mRightMouseTargetButton = NULL;
+ BOOST_FOREACH(LLToolBarButton* button, mButtons)
+ {
+ LLRect button_rect;
+ button->localRectToOtherView(button->getLocalRect(), &button_rect, this);
+
+ if (button_rect.pointInRect(x, y))
+ {
+ mRightMouseTargetButton = button;
+ break;
+ }
+ }
+
+ createContextMenu();
+
+ LLContextMenu * menu = (LLContextMenu *) mPopupMenuHandle.get();
+
+ if (menu)
+ {
+ menu->show(x, y);
+
+ LLMenuGL::showPopup(this, menu, x, y);
+ }
+ }
+
+ return handle_it_here;
+}
+
+BOOL LLToolBar::isSettingChecked(const LLSD& userdata)
+{
+ BOOL retval = FALSE;
+
+ const std::string setting_name = userdata.asString();
+
+ if (setting_name == "icons_with_text")
+ {
+ retval = (mButtonType == BTNTYPE_ICONS_WITH_TEXT);
+ }
+ else if (setting_name == "icons_only")
+ {
+ retval = (mButtonType == BTNTYPE_ICONS_ONLY);
+ }
+
+ return retval;
+}
+
+void LLToolBar::onSettingEnable(const LLSD& userdata)
+{
+ llassert(!mReadOnly);
+
+ const std::string setting_name = userdata.asString();
+
+ if (setting_name == "icons_with_text")
+ {
+ setButtonType(BTNTYPE_ICONS_WITH_TEXT);
+ }
+ else if (setting_name == "icons_only")
+ {
+ setButtonType(BTNTYPE_ICONS_ONLY);
+ }
+}
+
+void LLToolBar::onRemoveSelectedCommand()
+{
+ llassert(!mReadOnly);
+
+ if (mRightMouseTargetButton)
+ {
+ removeCommand(mRightMouseTargetButton->getCommandId());
+
+ mRightMouseTargetButton = NULL;
+ }
+}
+
+void LLToolBar::setButtonType(LLToolBarEnums::ButtonType button_type)
+{
+ bool regenerate_buttons = (mButtonType != button_type);
+
+ mButtonType = button_type;
+
+ if (regenerate_buttons)
+ {
+ createButtons();
+ }
+}
+
+void LLToolBar::resizeButtonsInRow(std::vector<LLToolBarButton*>& buttons_in_row, S32 max_row_girth)
+{
+ // make buttons in current row all same girth
+ BOOST_FOREACH(LLToolBarButton* button, buttons_in_row)
+ {
+ if (getOrientation(mSideType) == LLLayoutStack::HORIZONTAL)
+ {
+ button->reshape(button->mWidthRange.clamp(button->getRect().getWidth()), max_row_girth);
+ }
+ else // VERTICAL
+ {
+ button->reshape(max_row_girth, button->getRect().getHeight());
+ }
+ }
+}
+
+// Returns the position of the coordinates as a rank in the button list.
+// The rank is the position a tool dropped in (x,y) would assume in the button list.
+// The returned value is between 0 and mButtons.size(), 0 being the first element to the left
+// (or top) and mButtons.size() the last one to the right (or bottom).
+// Various drag data are stored in the toolbar object though are not exposed outside (and shouldn't).
+int LLToolBar::getRankFromPosition(S32 x, S32 y)
+{
+ if (mButtons.empty())
+ {
+ return RANK_NONE;
+ }
+
+ int rank = 0;
+
+ // Convert the toolbar coord into button panel coords
+ LLView::EOrientation orientation = getOrientation(mSideType);
+ S32 button_panel_x = 0;
+ S32 button_panel_y = 0;
+ localPointToOtherView(x, y, &button_panel_x, &button_panel_y, mButtonPanel);
+ S32 dx = x - button_panel_x;
+ S32 dy = y - button_panel_y;
+
+ // Simply compare the passed coord with the buttons outbound box + padding
+ std::list<LLToolBarButton*>::iterator it_button = mButtons.begin();
+ std::list<LLToolBarButton*>::iterator end_button = mButtons.end();
+ LLRect button_rect;
+ while (it_button != end_button)
+ {
+ button_rect = (*it_button)->getRect();
+ S32 point_x = button_rect.mRight + mPadRight;
+ S32 point_y = button_rect.mBottom - mPadBottom;
+
+ if ((button_panel_x < point_x) && (button_panel_y > point_y))
+ {
+ break;
+ }
+ rank++;
+ ++it_button;
+ }
+
+ // Update the passed coordinates to the hit button relevant corner
+ // (different depending on toolbar orientation)
+ if (rank < mButtons.size())
+ {
+ if (orientation == LLLayoutStack::HORIZONTAL)
+ {
+ // Horizontal
+ S32 mid_point = (button_rect.mRight + button_rect.mLeft) / 2;
+ if (button_panel_x < mid_point)
+ {
+ mDragx = button_rect.mLeft - mPadLeft;
+ mDragy = button_rect.mTop + mPadTop;
+ }
+ else
+ {
+ rank++;
+ mDragx = button_rect.mRight + mPadRight - 1;
+ mDragy = button_rect.mTop + mPadTop;
+ }
+ }
+ else
+ {
+ // Vertical
+ S32 mid_point = (button_rect.mTop + button_rect.mBottom) / 2;
+ if (button_panel_y > mid_point)
+ {
+ mDragx = button_rect.mLeft - mPadLeft;
+ mDragy = button_rect.mTop + mPadTop;
+ }
+ else
+ {
+ rank++;
+ mDragx = button_rect.mLeft - mPadLeft;
+ mDragy = button_rect.mBottom - mPadBottom + 1;
+ }
+ }
+ }
+ else
+ {
+ // We hit passed the end of the list so put the insertion point at the end
+ if (orientation == LLLayoutStack::HORIZONTAL)
+ {
+ mDragx = button_rect.mRight + mPadRight;
+ mDragy = button_rect.mTop + mPadTop;
+ }
+ else
+ {
+ mDragx = button_rect.mLeft - mPadLeft;
+ mDragy = button_rect.mBottom - mPadBottom;
+ }
+ }
+
+ // Update the "girth" of the caret, i.e. the width or height (depending of orientation)
+ if (orientation == LLLayoutStack::HORIZONTAL)
+ {
+ mDragGirth = button_rect.getHeight() + mPadBottom + mPadTop;
+ }
+ else
+ {
+ mDragGirth = button_rect.getWidth() + mPadLeft + mPadRight;
+ }
+
+ // The delta account for the coord model change (i.e. convert back to toolbar coord)
+ mDragx += dx;
+ mDragy += dy;
+
+ return rank;
+}
+
+int LLToolBar::getRankFromPosition(const LLCommandId& id)
+{
+ if (!hasCommand(id))
+ {
+ return RANK_NONE;
+ }
+ int rank = 0;
+ std::list<LLToolBarButton*>::iterator it_button = mButtons.begin();
+ std::list<LLToolBarButton*>::iterator end_button = mButtons.end();
+ while (it_button != end_button)
+ {
+ if ((*it_button)->mId == id)
+ {
+ break;
+ }
+ rank++;
+ ++it_button;
+ }
+ return rank;
+}
+
+void LLToolBar::updateLayoutAsNeeded()
+{
+ if (!mNeedsLayout) return;
+
+ LLView::EOrientation orientation = getOrientation(mSideType);
+
+ // our terminology for orientation-agnostic layout is such that
+ // length refers to a distance in the direction we stack the buttons
+ // and girth refers to a distance in the direction buttons wrap
+ S32 max_row_girth = 0;
+ S32 max_row_length = 0;
+
+ S32 max_length;
+ S32 cur_start;
+ S32 cur_row ;
+ S32 row_pad_start;
+ S32 row_pad_end;
+ S32 girth_pad_end;
+ S32 row_running_length;
+
+ if (orientation == LLLayoutStack::HORIZONTAL)
+ {
+ max_length = getRect().getWidth() - mPadLeft - mPadRight;
+ row_pad_start = mPadLeft;
+ row_pad_end = mPadRight;
+ cur_row = mPadTop;
+ girth_pad_end = mPadBottom;
+ }
+ else // VERTICAL
+ {
+ max_length = getRect().getHeight() - mPadTop - mPadBottom;
+ row_pad_start = mPadTop;
+ row_pad_end = mPadBottom;
+ cur_row = mPadLeft;
+ girth_pad_end = mPadRight;
+ }
+
+ row_running_length = row_pad_start;
+ cur_start = row_pad_start;
+
+
+ LLRect panel_rect = mButtonPanel->getLocalRect();
+
+ std::vector<LLToolBarButton*> buttons_in_row;
+
+ BOOST_FOREACH(LLToolBarButton* button, mButtons)
+ {
+ button->reshape(button->mWidthRange.getMin(), button->mDesiredHeight);
+ button->autoResize();
+
+ S32 button_clamped_width = button->mWidthRange.clamp(button->getRect().getWidth());
+ S32 button_length = (orientation == LLLayoutStack::HORIZONTAL)
+ ? button_clamped_width
+ : button->getRect().getHeight();
+ S32 button_girth = (orientation == LLLayoutStack::HORIZONTAL)
+ ? button->getRect().getHeight()
+ : button_clamped_width;
+
+ // wrap if needed
+ if (mWrap
+ && row_running_length + button_length > max_length // out of room...
+ && cur_start != row_pad_start) // ...and not first button in row
+ {
+ if (orientation == LLLayoutStack::VERTICAL)
+ { // row girth (width in this case) is clamped to allowable button widths
+ max_row_girth = button->mWidthRange.clamp(max_row_girth);
+ }
+
+ // make buttons in current row all same girth
+ resizeButtonsInRow(buttons_in_row, max_row_girth);
+ buttons_in_row.clear();
+
+ max_row_length = llmax(max_row_length, row_running_length);
+ row_running_length = row_pad_start;
+ cur_start = row_pad_start;
+ cur_row += max_row_girth + mPadBetween;
+ max_row_girth = 0;
+ }
+
+ LLRect button_rect;
+ if (orientation == LLLayoutStack::HORIZONTAL)
+ {
+ button_rect.setLeftTopAndSize(cur_start, panel_rect.mTop - cur_row, button_clamped_width, button->getRect().getHeight());
+ }
+ else // VERTICAL
+ {
+ button_rect.setLeftTopAndSize(cur_row, panel_rect.mTop - cur_start, button_clamped_width, button->getRect().getHeight());
+ }
+ button->setShape(button_rect);
+
+ buttons_in_row.push_back(button);
+
+ row_running_length += button_length + mPadBetween;
+ cur_start = row_running_length;
+ max_row_girth = llmax(button_girth, max_row_girth);
+ }
+
+ // final resizing in "girth" direction
+ S32 total_girth = cur_row // current row position...
+ + max_row_girth // ...incremented by size of final row...
+ + girth_pad_end; // ...plus padding reserved on end
+ total_girth = llmax(total_girth,mMinGirth);
+
+ max_row_length = llmax(max_row_length, row_running_length - mPadBetween + row_pad_end);
+
+ resizeButtonsInRow(buttons_in_row, max_row_girth);
+
+ // grow and optionally shift toolbar to accommodate buttons
+ if (orientation == LLLayoutStack::HORIZONTAL)
+ {
+ if (mSideType == SIDE_TOP)
+ { // shift down to maintain top edge
+ translate(0, getRect().getHeight() - total_girth);
+ }
+
+ reshape(getRect().getWidth(), total_girth);
+ mButtonPanel->reshape(max_row_length, total_girth);
+ }
+ else // VERTICAL
+ {
+ if (mSideType == SIDE_RIGHT)
+ { // shift left to maintain right edge
+ translate(getRect().getWidth() - total_girth, 0);
+ }
+
+ reshape(total_girth, getRect().getHeight());
+ mButtonPanel->reshape(total_girth, max_row_length);
+ }
+
+ // make parent fit button panel
+ mButtonPanel->getParent()->setShape(mButtonPanel->getLocalRect());
+
+ // re-center toolbar buttons
+ mCenteringStack->updateLayout();
+
+ if (!mButtons.empty())
+ {
+ mButtonPanel->setVisible(TRUE);
+ mButtonPanel->setMouseOpaque(TRUE);
+ }
+
+ // don't clear flag until after we've resized ourselves, to avoid laying out every frame
+ mNeedsLayout = false;
+}
+
+
+void LLToolBar::draw()
+{
+ if (mButtons.empty())
+ {
+ mButtonPanel->setVisible(FALSE);
+ mButtonPanel->setMouseOpaque(FALSE);
+ }
+ else
+ {
+ mButtonPanel->setVisible(TRUE);
+ mButtonPanel->setMouseOpaque(TRUE);
+ }
+
+ // Update enable/disable state and highlight state for editable toolbars
+ if (!mReadOnly)
+ {
+ for (toolbar_button_list::iterator btn_it = mButtons.begin(); btn_it != mButtons.end(); ++btn_it)
+ {
+ LLToolBarButton* btn = *btn_it;
+ LLCommand* command = LLCommandManager::instance().getCommand(btn->mId);
+
+ if (command && btn->mIsEnabledSignal)
+ {
+ const bool button_command_enabled = (*btn->mIsEnabledSignal)(btn, command->isEnabledParameters());
+ btn->setEnabled(button_command_enabled);
+ }
+
+ if (command && btn->mIsRunningSignal)
+ {
+ const bool button_command_running = (*btn->mIsRunningSignal)(btn, command->isRunningParameters());
+ btn->setToggleState(button_command_running);
+ }
+ }
+ }
+
+ updateLayoutAsNeeded();
+ // rect may have shifted during layout
+ LLUI::popMatrix();
+ LLUI::pushMatrix();
+ LLUI::translate((F32)getRect().mLeft, (F32)getRect().mBottom);
+
+ // Position the caret
+ if (!mCaretIcon)
+ {
+ mCaretIcon = getChild<LLIconCtrl>("caret");
+ }
+
+ LLIconCtrl* caret = mCaretIcon;
+ caret->setVisible(FALSE);
+ if (mDragAndDropTarget && !mButtonCommands.empty())
+ {
+ LLRect caret_rect = caret->getRect();
+ if (getOrientation(mSideType) == LLLayoutStack::HORIZONTAL)
+ {
+ caret->setRect(LLRect(mDragx-caret_rect.getWidth()/2+1,
+ mDragy,
+ mDragx+caret_rect.getWidth()/2+1,
+ mDragy-mDragGirth));
+ }
+ else
+ {
+ caret->setRect(LLRect(mDragx,
+ mDragy+caret_rect.getHeight()/2,
+ mDragx+mDragGirth,
+ mDragy-caret_rect.getHeight()/2));
+ }
+ caret->setVisible(TRUE);
+ }
+
+ LLUICtrl::draw();
+ caret->setVisible(FALSE);
+ mDragAndDropTarget = false;
+}
+
+void LLToolBar::reshape(S32 width, S32 height, BOOL called_from_parent)
+{
+ LLUICtrl::reshape(width, height, called_from_parent);
+ mNeedsLayout = true;
+}
+
+void LLToolBar::createButtons()
+{
+ std::set<LLUUID> set_flashing;
+
+ BOOST_FOREACH(LLToolBarButton* button, mButtons)
+ {
+ if (button->getFlashTimer() && button->getFlashTimer()->isFlashingInProgress())
+ {
+ set_flashing.insert(button->getCommandId().uuid());
+ }
+
+ if (mButtonRemoveSignal)
+ {
+ (*mButtonRemoveSignal)(button);
+ }
+
+ delete button;
+ }
+ mButtons.clear();
+ mButtonMap.clear();
+ mRightMouseTargetButton = NULL;
+
+ BOOST_FOREACH(LLCommandId& command_id, mButtonCommands)
+ {
+ LLToolBarButton* button = createButton(command_id);
+ mButtons.push_back(button);
+ mButtonPanel->addChild(button);
+ mButtonMap.insert(std::make_pair(command_id.uuid(), button));
+
+ if (mButtonAddSignal)
+ {
+ (*mButtonAddSignal)(button);
+ }
+
+ if (set_flashing.find(button->getCommandId().uuid()) != set_flashing.end())
+ {
+ button->setFlashing(true);
+ }
+ }
+ mNeedsLayout = true;
+}
+
+void LLToolBarButton::callIfEnabled(LLUICtrl::commit_callback_t commit, LLUICtrl* ctrl, const LLSD& param )
+{
+ LLCommand* command = LLCommandManager::instance().getCommand(mId);
+
+ if (!mIsEnabledSignal || (*mIsEnabledSignal)(this, command->isEnabledParameters()))
+ {
+ commit(ctrl, param);
+ }
+}
+
+LLToolBarButton* LLToolBar::createButton(const LLCommandId& id)
+{
+ LLCommand* commandp = LLCommandManager::instance().getCommand(id);
+ if (!commandp) return NULL;
+
+ LLToolBarButton::Params button_p;
+ button_p.name = commandp->name();
+ button_p.label = LLTrans::getString(commandp->labelRef());
+ button_p.tool_tip = LLTrans::getString(commandp->tooltipRef());
+ button_p.image_overlay = LLUI::getUIImage(commandp->icon());
+ button_p.button_flash_enable = commandp->isFlashingAllowed();
+ button_p.overwriteFrom(mButtonParams[mButtonType]);
+ LLToolBarButton* button = LLUICtrlFactory::create<LLToolBarButton>(button_p);
+
+ if (!mReadOnly)
+ {
+ enable_callback_t isEnabledCB;
+
+ const std::string& isEnabledFunction = commandp->isEnabledFunctionName();
+ if (isEnabledFunction.length() > 0)
+ {
+ LLUICtrl::EnableCallbackParam isEnabledParam;
+ isEnabledParam.function_name = isEnabledFunction;
+ isEnabledParam.parameter = commandp->isEnabledParameters();
+ isEnabledCB = initEnableCallback(isEnabledParam);
+
+ if (NULL == button->mIsEnabledSignal)
+ {
+ button->mIsEnabledSignal = new enable_signal_t();
+ }
+
+ button->mIsEnabledSignal->connect(isEnabledCB);
+ }
+
+ LLUICtrl::CommitCallbackParam executeParam;
+ executeParam.function_name = commandp->executeFunctionName();
+ executeParam.parameter = commandp->executeParameters();
+
+ // If we have a "stop" function then we map the command to mouse down / mouse up otherwise commit
+ const std::string& executeStopFunction = commandp->executeStopFunctionName();
+ if (executeStopFunction.length() > 0)
+ {
+ LLUICtrl::CommitCallbackParam executeStopParam;
+ executeStopParam.function_name = executeStopFunction;
+ executeStopParam.parameter = commandp->executeStopParameters();
+ LLUICtrl::commit_callback_t execute_func = initCommitCallback(executeParam);
+ LLUICtrl::commit_callback_t stop_func = initCommitCallback(executeStopParam);
+
+ button->setMouseDownCallback(boost::bind(&LLToolBarButton::callIfEnabled, button, execute_func, _1, _2));
+ button->setMouseUpCallback(boost::bind(&LLToolBarButton::callIfEnabled, button, stop_func, _1, _2));
+ }
+ else
+ {
+ button->setCommitCallback(executeParam);
+ }
+
+ // Set up "is running" query callback
+ const std::string& isRunningFunction = commandp->isRunningFunctionName();
+ if (isRunningFunction.length() > 0)
+ {
+ LLUICtrl::EnableCallbackParam isRunningParam;
+ isRunningParam.function_name = isRunningFunction;
+ isRunningParam.parameter = commandp->isRunningParameters();
+ enable_signal_t::slot_type isRunningCB = initEnableCallback(isRunningParam);
+
+ if (NULL == button->mIsRunningSignal)
+ {
+ button->mIsRunningSignal = new enable_signal_t();
+ }
+
+ button->mIsRunningSignal->connect(isRunningCB);
+ }
+ }
+
+ // Drag and drop behavior must work also if provided in the Toybox and, potentially, any read-only toolbar
+ button->setStartDragCallback(mStartDragItemCallback);
+ button->setHandleDragCallback(mHandleDragItemCallback);
+
+ button->setCommandId(id);
+
+ return button;
+}
+
+boost::signals2::connection connectSignal(LLToolBar::button_signal_t*& signal, const LLToolBar::button_signal_t::slot_type& cb)
+{
+ if (!signal)
+ {
+ signal = new LLToolBar::button_signal_t();
+ }
+
+ return signal->connect(cb);
+}
+
+boost::signals2::connection LLToolBar::setButtonAddCallback(const button_signal_t::slot_type& cb)
+{
+ return connectSignal(mButtonAddSignal, cb);
+}
+
+boost::signals2::connection LLToolBar::setButtonEnterCallback(const button_signal_t::slot_type& cb)
+{
+ return connectSignal(mButtonEnterSignal, cb);
+}
+
+boost::signals2::connection LLToolBar::setButtonLeaveCallback(const button_signal_t::slot_type& cb)
+{
+ return connectSignal(mButtonLeaveSignal, cb);
+}
+
+boost::signals2::connection LLToolBar::setButtonRemoveCallback(const button_signal_t::slot_type& cb)
+{
+ return connectSignal(mButtonRemoveSignal, cb);
+}
+
+BOOL LLToolBar::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ std::string& tooltip_msg)
+{
+ // If we have a drop callback, that means that we can handle the drop
+ BOOL handled = (mHandleDropCallback ? TRUE : FALSE);
+
+ // if drop is set, it's time to call the callback to get the operation done
+ if (handled && drop)
+ {
+ handled = mHandleDropCallback(cargo_data, x, y ,this);
+ }
+
+ // We accept only single tool drop on toolbars
+ *accept = (handled ? ACCEPT_YES_SINGLE : ACCEPT_NO);
+
+ // We'll use that flag to change the visual aspect of the toolbar target on draw()
+ mDragAndDropTarget = false;
+
+ // Convert drag position into insert position and rank
+ if (!isReadOnly() && handled && !drop)
+ {
+ if (cargo_type == DAD_WIDGET)
+ {
+ LLInventoryItem* inv_item = (LLInventoryItem*)cargo_data;
+ LLCommandId dragged_command(inv_item->getUUID());
+ int orig_rank = getRankFromPosition(dragged_command);
+ mDragRank = getRankFromPosition(x, y);
+ // Don't DaD if we're dragging a command on itself
+ mDragAndDropTarget = ((orig_rank != RANK_NONE) && ((mDragRank == orig_rank) || ((mDragRank-1) == orig_rank)) ? false : true);
+ //LL_INFOS() << "Merov debug : DaD, rank = " << mDragRank << ", dragged uui = " << inv_item->getUUID() << LL_ENDL;
+ /* Do the following if you want to animate the button itself
+ LLCommandId dragged_command(inv_item->getUUID());
+ removeCommand(dragged_command);
+ addCommand(dragged_command,rank);
+ */
+ }
+ else
+ {
+ handled = FALSE;
+ }
+ }
+
+ return handled;
+}
+
+LLToolBarButton::LLToolBarButton(const Params& p)
+: LLButton(p),
+ mMouseDownX(0),
+ mMouseDownY(0),
+ mWidthRange(p.button_width),
+ mDesiredHeight(p.desired_height),
+ mId(""),
+ mIsEnabledSignal(NULL),
+ mIsRunningSignal(NULL),
+ mIsStartingSignal(NULL),
+ mIsDragged(false),
+ mStartDragItemCallback(NULL),
+ mHandleDragItemCallback(NULL),
+ mOriginalImageSelected(p.image_selected),
+ mOriginalImageUnselected(p.image_unselected),
+ mOriginalImagePressed(p.image_pressed),
+ mOriginalImagePressedSelected(p.image_pressed_selected),
+ mOriginalLabelColor(p.label_color),
+ mOriginalLabelColorSelected(p.label_color_selected),
+ mOriginalImageOverlayColor(p.image_overlay_color),
+ mOriginalImageOverlaySelectedColor(p.image_overlay_selected_color)
+{
+}
+
+LLToolBarButton::~LLToolBarButton()
+{
+ delete mIsEnabledSignal;
+ delete mIsRunningSignal;
+ delete mIsStartingSignal;
+}
+
+BOOL LLToolBarButton::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ mMouseDownX = x;
+ mMouseDownY = y;
+ return LLButton::handleMouseDown(x, y, mask);
+}
+
+BOOL LLToolBarButton::handleHover(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = FALSE;
+
+ S32 mouse_distance_squared = (x - mMouseDownX) * (x - mMouseDownX) + (y - mMouseDownY) * (y - mMouseDownY);
+ S32 drag_threshold = LLUI::sSettingGroups["config"]->getS32("DragAndDropDistanceThreshold");
+ if (mouse_distance_squared > drag_threshold * drag_threshold
+ && hasMouseCapture() &&
+ mStartDragItemCallback && mHandleDragItemCallback)
+ {
+ if (!mIsDragged)
+ {
+ mStartDragItemCallback(x, y, this);
+ mIsDragged = true;
+ handled = TRUE;
+ }
+ else
+ {
+ handled = mHandleDragItemCallback(x, y, mId.uuid(), LLAssetType::AT_WIDGET);
+ }
+ }
+ else
+ {
+ handled = LLButton::handleHover(x, y, mask);
+ }
+
+ return handled;
+}
+
+void LLToolBarButton::onMouseEnter(S32 x, S32 y, MASK mask)
+{
+ LLUICtrl::onMouseEnter(x, y, mask);
+
+ // Always highlight toolbar buttons, even if they are disabled
+ if (!gFocusMgr.getMouseCapture() || gFocusMgr.getMouseCapture() == this)
+ {
+ mNeedsHighlight = TRUE;
+ }
+
+ LLToolBar* parent_toolbar = getParentByType<LLToolBar>();
+ if (parent_toolbar && parent_toolbar->mButtonEnterSignal)
+ {
+ (*(parent_toolbar->mButtonEnterSignal))(this);
+ }
+}
+
+void LLToolBarButton::onMouseLeave(S32 x, S32 y, MASK mask)
+{
+ LLButton::onMouseLeave(x, y, mask);
+
+ LLToolBar* parent_toolbar = getParentByType<LLToolBar>();
+ if (parent_toolbar && parent_toolbar->mButtonLeaveSignal)
+ {
+ (*(parent_toolbar->mButtonLeaveSignal))(this);
+ }
+}
+
+void LLToolBarButton::onMouseCaptureLost()
+{
+ mIsDragged = false;
+}
+
+void LLToolBarButton::onCommit()
+{
+ LLCommand* command = LLCommandManager::instance().getCommand(mId);
+
+ if (!mIsEnabledSignal || (*mIsEnabledSignal)(this, command->isEnabledParameters()))
+ {
+ LLButton::onCommit();
+ }
+}
+
+void LLToolBarButton::reshape(S32 width, S32 height, BOOL called_from_parent)
+{
+ LLButton::reshape(mWidthRange.clamp(width), height, called_from_parent);
+}
+
+void LLToolBarButton::setEnabled(BOOL enabled)
+{
+ if (enabled)
+ {
+ mImageSelected = mOriginalImageSelected;
+ mImageUnselected = mOriginalImageUnselected;
+ mImagePressed = mOriginalImagePressed;
+ mImagePressedSelected = mOriginalImagePressedSelected;
+ mUnselectedLabelColor = mOriginalLabelColor;
+ mSelectedLabelColor = mOriginalLabelColorSelected;
+ mImageOverlayColor = mOriginalImageOverlayColor;
+ mImageOverlaySelectedColor = mOriginalImageOverlaySelectedColor;
+ }
+ else
+ {
+ mImageSelected = mImageDisabledSelected;
+ mImageUnselected = mImageDisabled;
+ mImagePressed = mImageDisabled;
+ mImagePressedSelected = mImageDisabledSelected;
+ mUnselectedLabelColor = mDisabledLabelColor;
+ mSelectedLabelColor = mDisabledSelectedLabelColor;
+ mImageOverlayColor = mImageOverlayDisabledColor;
+ mImageOverlaySelectedColor = mImageOverlayDisabledColor;
+ }
+}
+
+const std::string LLToolBarButton::getToolTip() const
+{
+ std::string tooltip;
+
+ if (labelIsTruncated() || getCurrentLabel().empty())
+ {
+ tooltip = LLTrans::getString(LLCommandManager::instance().getCommand(mId)->labelRef()) + " -- " + LLView::getToolTip();
+ }
+ else
+ {
+ tooltip = LLView::getToolTip();
+ }
+
+ LLToolBar* parent_toolbar = getParentByType<LLToolBar>();
+ if (parent_toolbar && parent_toolbar->mButtonTooltipSuffix.length() > 0)
+ {
+ tooltip = tooltip + "\n(" + parent_toolbar->mButtonTooltipSuffix + ")";
+ }
+
+ return tooltip;
+}
+
+void LLToolBar::LLCenterLayoutPanel::handleReshape(const LLRect& rect, bool by_user)
+{
+ LLLayoutPanel::handleReshape(rect, by_user);
+
+ if (!mReshapeCallback.empty())
+ {
+ LLRect r;
+ localRectToOtherView(mButtonPanel->getRect(), &r, gFloaterView);
+ r.stretch(FLOATER_MIN_VISIBLE_PIXELS);
+ mReshapeCallback(mLocationId, r);
+ }
+}
diff --git a/indra/llui/lltoolbar.h b/indra/llui/lltoolbar.h
new file mode 100755
index 0000000000..370941c787
--- /dev/null
+++ b/indra/llui/lltoolbar.h
@@ -0,0 +1,331 @@
+/**
+ * @file lltoolbar.h
+ * @author Richard Nelson
+ * @brief User customizable toolbar class
+ *
+ * $LicenseInfo:firstyear=2011&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2011, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLTOOLBAR_H
+#define LL_LLTOOLBAR_H
+
+#include "llbutton.h"
+#include "llcommandmanager.h"
+#include "lllayoutstack.h"
+#include "lluictrl.h"
+#include "llcommandmanager.h"
+#include "llassettype.h"
+
+class LLToolBar;
+class LLToolBarButton;
+class LLIconCtrl;
+
+typedef boost::function<void (S32 x, S32 y, LLToolBarButton* button)> tool_startdrag_callback_t;
+typedef boost::function<BOOL (S32 x, S32 y, const LLUUID& uuid, LLAssetType::EType type)> tool_handledrag_callback_t;
+typedef boost::function<BOOL (void* data, S32 x, S32 y, LLToolBar* toolbar)> tool_handledrop_callback_t;
+
+class LLToolBarButton : public LLButton
+{
+ friend class LLToolBar;
+public:
+ struct Params : public LLInitParam::Block<Params, LLButton::Params>
+ {
+ Optional<LLUI::RangeS32::Params> button_width;
+ Optional<S32> desired_height;
+
+ Params()
+ : button_width("button_width"),
+ desired_height("desired_height", 20)
+ {}
+
+ };
+
+ LLToolBarButton(const Params& p);
+ ~LLToolBarButton();
+
+ BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ BOOL handleHover(S32 x, S32 y, MASK mask);
+
+ void reshape(S32 width, S32 height, BOOL called_from_parent = true);
+ void setEnabled(BOOL enabled);
+ void setCommandId(const LLCommandId& id) { mId = id; }
+ LLCommandId getCommandId() { return mId; }
+
+ void setStartDragCallback(tool_startdrag_callback_t cb) { mStartDragItemCallback = cb; }
+ void setHandleDragCallback(tool_handledrag_callback_t cb) { mHandleDragItemCallback = cb; }
+
+ void onMouseEnter(S32 x, S32 y, MASK mask);
+ void onMouseLeave(S32 x, S32 y, MASK mask);
+ void onMouseCaptureLost();
+
+ void onCommit();
+
+ virtual const std::string getToolTip() const;
+
+private:
+ void callIfEnabled(LLUICtrl::commit_callback_t commit, LLUICtrl* ctrl, const LLSD& param );
+
+ LLCommandId mId;
+ S32 mMouseDownX;
+ S32 mMouseDownY;
+ LLUI::RangeS32 mWidthRange;
+ S32 mDesiredHeight;
+ bool mIsDragged;
+ tool_startdrag_callback_t mStartDragItemCallback;
+ tool_handledrag_callback_t mHandleDragItemCallback;
+
+ enable_signal_t* mIsEnabledSignal;
+ enable_signal_t* mIsRunningSignal;
+ enable_signal_t* mIsStartingSignal;
+ LLPointer<LLUIImage> mOriginalImageSelected,
+ mOriginalImageUnselected,
+ mOriginalImagePressed,
+ mOriginalImagePressedSelected;
+ LLUIColor mOriginalLabelColor,
+ mOriginalLabelColorSelected,
+ mOriginalImageOverlayColor,
+ mOriginalImageOverlaySelectedColor;
+};
+
+
+namespace LLToolBarEnums
+{
+ enum ButtonType
+ {
+ BTNTYPE_ICONS_WITH_TEXT = 0,
+ BTNTYPE_ICONS_ONLY,
+
+ BTNTYPE_COUNT
+ };
+
+ enum SideType
+ {
+ SIDE_BOTTOM,
+ SIDE_LEFT,
+ SIDE_RIGHT,
+ SIDE_TOP,
+ };
+
+ enum EToolBarLocation
+ {
+ TOOLBAR_NONE = 0,
+ TOOLBAR_LEFT,
+ TOOLBAR_RIGHT,
+ TOOLBAR_BOTTOM,
+
+ TOOLBAR_COUNT,
+
+ TOOLBAR_FIRST = TOOLBAR_LEFT,
+ TOOLBAR_LAST = TOOLBAR_BOTTOM,
+ };
+
+ LLView::EOrientation getOrientation(SideType sideType);
+}
+
+// NOTE: This needs to occur before Param block declaration for proper compilation.
+namespace LLInitParam
+{
+ template<>
+ struct TypeValues<LLToolBarEnums::ButtonType> : public TypeValuesHelper<LLToolBarEnums::ButtonType>
+ {
+ static void declareValues();
+ };
+
+ template<>
+ struct TypeValues<LLToolBarEnums::SideType> : public TypeValuesHelper<LLToolBarEnums::SideType>
+ {
+ static void declareValues();
+ };
+}
+
+
+class LLToolBar
+: public LLUICtrl
+{
+ friend class LLToolBarButton;
+public:
+
+ class LLCenterLayoutPanel : public LLLayoutPanel
+ {
+ public:
+ typedef boost::function<void(LLToolBarEnums::EToolBarLocation tb, const LLRect& rect)> reshape_callback_t;
+
+ virtual ~LLCenterLayoutPanel() {}
+ /*virtual*/ void handleReshape(const LLRect& rect, bool by_user);
+
+ void setLocationId(LLToolBarEnums::EToolBarLocation id) { mLocationId = id; }
+ void setReshapeCallback(reshape_callback_t cb) { mReshapeCallback = cb; }
+ void setButtonPanel(LLPanel * panel) { mButtonPanel = panel; }
+
+ protected:
+ friend class LLUICtrlFactory;
+ LLCenterLayoutPanel(const Params& params) : LLLayoutPanel(params), mButtonPanel(NULL) {}
+
+ private:
+ reshape_callback_t mReshapeCallback;
+ LLToolBarEnums::EToolBarLocation mLocationId;
+ LLPanel * mButtonPanel;
+ };
+
+ struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
+ {
+ Mandatory<LLToolBarEnums::ButtonType> button_display_mode;
+ Mandatory<LLToolBarEnums::SideType> side;
+
+ Optional<LLToolBarButton::Params> button_icon,
+ button_icon_and_text;
+
+ Optional<bool> read_only,
+ wrap;
+
+ Optional<S32> pad_left,
+ pad_top,
+ pad_right,
+ pad_bottom,
+ pad_between,
+ min_girth;
+
+ // default command set
+ Multiple<LLCommandId::Params> commands;
+
+ Optional<LLPanel::Params> button_panel;
+
+ Params();
+ };
+
+ // virtuals
+ void draw();
+ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
+ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ std::string& tooltip_msg);
+
+ static const int RANK_NONE = -1;
+ bool addCommand(const LLCommandId& commandId, int rank = RANK_NONE);
+ int removeCommand(const LLCommandId& commandId); // Returns the rank the removed command was at, RANK_NONE if not found
+ bool hasCommand(const LLCommandId& commandId) const; // is this command bound to a button in this toolbar
+ bool enableCommand(const LLCommandId& commandId, bool enabled); // enable/disable button bound to the specified command, if it exists in this toolbar
+ bool stopCommandInProgress(const LLCommandId& commandId); // stop command if it is currently active
+ bool flashCommand(const LLCommandId& commandId, bool flash, bool force_flashing = false); // flash button associated with given command, if in this toolbar
+
+ void setStartDragCallback(tool_startdrag_callback_t cb) { mStartDragItemCallback = cb; } // connects drag and drop behavior to external logic
+ void setHandleDragCallback(tool_handledrag_callback_t cb) { mHandleDragItemCallback = cb; }
+ void setHandleDropCallback(tool_handledrop_callback_t cb) { mHandleDropCallback = cb; }
+ bool isReadOnly() const { return mReadOnly; }
+ LLCenterLayoutPanel * getCenterLayoutPanel() const { return mCenterPanel; }
+
+ LLToolBarButton* createButton(const LLCommandId& id);
+
+ typedef boost::signals2::signal<void (LLView* button)> button_signal_t;
+ boost::signals2::connection setButtonAddCallback(const button_signal_t::slot_type& cb);
+ boost::signals2::connection setButtonEnterCallback(const button_signal_t::slot_type& cb);
+ boost::signals2::connection setButtonLeaveCallback(const button_signal_t::slot_type& cb);
+ boost::signals2::connection setButtonRemoveCallback(const button_signal_t::slot_type& cb);
+
+ // append the specified string to end of tooltip
+ void setTooltipButtonSuffix(const std::string& suffix) { mButtonTooltipSuffix = suffix; }
+
+ LLToolBarEnums::SideType getSideType() const { return mSideType; }
+ bool hasButtons() const { return !mButtons.empty(); }
+ bool isModified() const { return mModified; }
+
+ int getRankFromPosition(S32 x, S32 y);
+ int getRankFromPosition(const LLCommandId& id);
+
+ // Methods used in loading and saving toolbar settings
+ void setButtonType(LLToolBarEnums::ButtonType button_type);
+ LLToolBarEnums::ButtonType getButtonType() { return mButtonType; }
+ command_id_list_t& getCommandsList() { return mButtonCommands; }
+ void clearCommandsList();
+
+private:
+ friend class LLUICtrlFactory;
+ LLToolBar(const Params&);
+ ~LLToolBar();
+
+ void initFromParams(const Params&);
+ void createContextMenu();
+ void updateLayoutAsNeeded();
+ void createButtons();
+ void resizeButtonsInRow(std::vector<LLToolBarButton*>& buttons_in_row, S32 max_row_girth);
+ BOOL isSettingChecked(const LLSD& userdata);
+ void onSettingEnable(const LLSD& userdata);
+ void onRemoveSelectedCommand();
+
+private:
+ // static layout state
+ const bool mReadOnly;
+ const LLToolBarEnums::SideType mSideType;
+ const bool mWrap;
+ const S32 mPadLeft,
+ mPadRight,
+ mPadTop,
+ mPadBottom,
+ mPadBetween,
+ mMinGirth;
+
+ // drag and drop state
+ tool_startdrag_callback_t mStartDragItemCallback;
+ tool_handledrag_callback_t mHandleDragItemCallback;
+ tool_handledrop_callback_t mHandleDropCallback;
+ bool mDragAndDropTarget;
+ int mDragRank;
+ S32 mDragx,
+ mDragy,
+ mDragGirth;
+
+ typedef std::list<LLToolBarButton*> toolbar_button_list;
+ typedef std::map<LLUUID, LLToolBarButton*> command_id_map;
+ toolbar_button_list mButtons;
+ command_id_list_t mButtonCommands;
+ command_id_map mButtonMap;
+
+ LLToolBarEnums::ButtonType mButtonType;
+ LLToolBarButton::Params mButtonParams[LLToolBarEnums::BTNTYPE_COUNT];
+
+ // related widgets
+ LLLayoutStack* mCenteringStack;
+ LLCenterLayoutPanel* mCenterPanel;
+ LLPanel* mButtonPanel;
+ LLHandle<class LLContextMenu> mPopupMenuHandle;
+ LLHandle<class LLView> mRemoveButtonHandle;
+
+ LLToolBarButton* mRightMouseTargetButton;
+
+ bool mNeedsLayout;
+ bool mModified;
+
+ button_signal_t* mButtonAddSignal;
+ button_signal_t* mButtonEnterSignal;
+ button_signal_t* mButtonLeaveSignal;
+ button_signal_t* mButtonRemoveSignal;
+
+ std::string mButtonTooltipSuffix;
+
+ LLIconCtrl* mCaretIcon;
+};
+
+
+#endif // LL_LLTOOLBAR_H
diff --git a/indra/llui/lltooltip.cpp b/indra/llui/lltooltip.cpp
index 6390039794..7f2224870d 100644..100755
--- a/indra/llui/lltooltip.cpp
+++ b/indra/llui/lltooltip.cpp
@@ -55,7 +55,7 @@ static LLDefaultChildRegistry::Register<LLToolTipView> register_tooltip_view("to
LLToolTipView::Params::Params()
{
- mouse_opaque = false;
+ changeDefault(mouse_opaque, false);
}
LLToolTipView::LLToolTipView(const LLToolTipView::Params& p)
@@ -156,7 +156,7 @@ LLToolTip::Params::Params()
web_based_media("web_based_media", false),
media_playing("media_playing", false)
{
- chrome = true;
+ changeDefault(chrome, true);
}
LLToolTip::LLToolTip(const LLToolTip::Params& p)
@@ -180,6 +180,7 @@ LLToolTip::LLToolTip(const LLToolTip::Params& p)
params.font = p.font;
params.use_ellipses = true;
params.wrap = p.wrap;
+ params.font_valign = LLFontGL::VCENTER;
params.parse_urls = false; // disallow hyperlinks in tooltips, as they want to spawn their own explanatory tooltips
mTextBox = LLUICtrlFactory::create<LLTextBox> (params);
addChild(mTextBox);
@@ -190,7 +191,6 @@ LLToolTip::LLToolTip(const LLToolTip::Params& p)
{
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);
@@ -200,7 +200,7 @@ LLToolTip::LLToolTip(const LLToolTip::Params& p)
icon_params.image_selected(imagep);
icon_params.scale_image(true);
- icon_params.flash_color(icon_params.highlight_color());
+ icon_params.flash_color.control = "ButtonUnselectedFgColor";
mInfoButton = LLUICtrlFactory::create<LLButton>(icon_params);
if (p.click_callback.isProvided())
{
@@ -288,9 +288,15 @@ void LLToolTip::initFromParams(const LLToolTip::Params& p)
mTextBox->setText(p.message());
}
- S32 text_width = llmin(p.max_width(), mTextBox->getTextPixelWidth());
+ S32 text_width = llmin(p.max_width(), mTextBox->getTextPixelWidth() + 1);
S32 text_height = mTextBox->getTextPixelHeight();
mTextBox->reshape(text_width, text_height);
+ if (mInfoButton)
+ {
+ LLRect text_rect = mTextBox->getRect();
+ LLRect icon_rect = mInfoButton->getRect();
+ mTextBox->translate(0, icon_rect.getCenterY() - text_rect.getCenterY());
+ }
// reshape tooltip panel to fit text box
LLRect tooltip_rect = calcBoundingRect();
@@ -299,6 +305,8 @@ void LLToolTip::initFromParams(const LLToolTip::Params& p)
tooltip_rect.mBottom = 0;
tooltip_rect.mLeft = 0;
+ mTextBox->reshape(mTextBox->getRect().getWidth(), llmax(mTextBox->getRect().getHeight(), tooltip_rect.getHeight() - 2 * mPadding));
+
setShape(tooltip_rect);
}
@@ -382,6 +390,15 @@ bool LLToolTip::hasClickCallback()
return mHasClickCallback;
}
+void LLToolTip::getToolTipMessage(std::string & message)
+{
+ if (mTextBox)
+ {
+ message = mTextBox->getText();
+ }
+}
+
+
//
// LLToolTipMgr
@@ -402,12 +419,12 @@ void LLToolTipMgr::createToolTip(const LLToolTip::Params& params)
LLToolTip::Params tooltip_params(params);
// block mouse events if there is a click handler registered (specifically, hover)
- if (params.click_callback.isProvided())
+ if (params.click_callback.isProvided() && !params.mouse_opaque.isProvided())
{
// set mouse_opaque to true if it wasn't already set to something else
// this prevents mouse down from going "through" the tooltip and ultimately
// causing the tooltip to disappear
- tooltip_params.mouse_opaque.setIfNotProvided(true);
+ tooltip_params.mouse_opaque = true;
}
tooltip_params.rect = LLRect (0, 1, 1, 0);
@@ -459,12 +476,16 @@ void LLToolTipMgr::show(const std::string& msg)
void LLToolTipMgr::show(const LLToolTip::Params& params)
{
+ if (!params.styled_message.isProvided()
+ && (!params.message.isProvided() || params.message().empty())
+ && !params.image.isProvided()) return;
+
// 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;
+ LL_WARNS() << "Could not display tooltip!" << LL_ENDL;
return;
}
@@ -586,5 +607,14 @@ void LLToolTipMgr::updateToolTipVisibility()
}
+// Return the current tooltip text
+void LLToolTipMgr::getToolTipMessage(std::string & message)
+{
+ if (toolTipVisible())
+ {
+ mToolTip->getToolTipMessage(message);
+ }
+}
+
// EOF
diff --git a/indra/llui/lltooltip.h b/indra/llui/lltooltip.h
index d71a944c3d..fad127fc4c 100644..100755
--- a/indra/llui/lltooltip.h
+++ b/indra/llui/lltooltip.h
@@ -105,6 +105,8 @@ public:
LLToolTip(const Params& p);
void initFromParams(const LLToolTip::Params& params);
+ void getToolTipMessage(std::string & message);
+
private:
class LLTextBox* mTextBox;
class LLButton* mInfoButton;
@@ -142,6 +144,8 @@ public:
LLRect getMouseNearRect();
void updateToolTipVisibility();
+ void getToolTipMessage(std::string & message);
+
private:
void createToolTip(const LLToolTip::Params& params);
diff --git a/indra/llui/lltrans.cpp b/indra/llui/lltrans.cpp
new file mode 100755
index 0000000000..4d4ff4236d
--- /dev/null
+++ b/indra/llui/lltrans.cpp
@@ -0,0 +1,295 @@
+/**
+ * @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 "llfasttimer.h" // for call count statistics
+#include "llxuiparser.h"
+#include "llsd.h"
+#include "llxmlnode.h"
+
+#include <map>
+
+LLTrans::template_map_t LLTrans::sStringTemplates;
+LLStringUtil::format_map_t LLTrans::sDefaultArgs;
+
+struct StringDef : public LLInitParam::Block<StringDef>
+{
+ Mandatory<std::string> name;
+ Mandatory<std::string> value;
+
+ StringDef()
+ : name("name"),
+ value("value")
+ {}
+};
+
+struct StringTable : public LLInitParam::Block<StringTable>
+{
+ Multiple<StringDef> strings;
+ StringTable()
+ : strings("string")
+ {}
+};
+
+//static
+bool LLTrans::parseStrings(LLXMLNodePtr &root, const std::set<std::string>& default_args)
+{
+ std::string xml_filename = "(strings file)";
+ if (!root->hasName("strings"))
+ {
+ LL_ERRS() << "Invalid root node name in " << xml_filename
+ << ": was " << root->getName() << ", expected \"strings\"" << LL_ENDL;
+ }
+
+ StringTable string_table;
+ LLXUIParser parser;
+ parser.readXUI(root, string_table, xml_filename);
+
+ if (!string_table.validateBlock())
+ {
+ LL_ERRS() << "Problem reading strings: " << xml_filename << LL_ENDL;
+ return false;
+ }
+
+ sStringTemplates.clear();
+ sDefaultArgs.clear();
+
+ for(LLInitParam::ParamIterator<StringDef>::const_iterator it = string_table.strings.begin();
+ it != string_table.strings.end();
+ ++it)
+ {
+ LLTransTemplate xml_template(it->name, it->value);
+ sStringTemplates[xml_template.mName] = xml_template;
+
+ std::set<std::string>::const_iterator iter = default_args.find(xml_template.mName);
+ if (iter != default_args.end())
+ {
+ std::string name = *iter;
+ if (name[0] != '[')
+ name = llformat("[%s]",name.c_str());
+ sDefaultArgs[name] = xml_template.mText;
+ }
+ }
+
+ return true;
+}
+
+
+//static
+bool LLTrans::parseLanguageStrings(LLXMLNodePtr &root)
+{
+ std::string xml_filename = "(language strings file)";
+ if (!root->hasName("strings"))
+ {
+ LL_ERRS() << "Invalid root node name in " << xml_filename
+ << ": was " << root->getName() << ", expected \"strings\"" << LL_ENDL;
+ }
+
+ StringTable string_table;
+ LLXUIParser parser;
+ parser.readXUI(root, string_table, xml_filename);
+
+ if (!string_table.validateBlock())
+ {
+ LL_ERRS() << "Problem reading strings: " << xml_filename << LL_ENDL;
+ return false;
+ }
+
+ for(LLInitParam::ParamIterator<StringDef>::const_iterator it = string_table.strings.begin();
+ it != string_table.strings.end();
+ ++it)
+ {
+ // share the same map with parseStrings() so we can search the strings using the same getString() function.- angela
+ LLTransTemplate xml_template(it->name, it->value);
+ sStringTemplates[xml_template.mName] = xml_template;
+ }
+
+ return true;
+}
+
+
+
+static LLTrace::BlockTimerStatHandle FTM_GET_TRANS("Translate string");
+
+//static
+std::string LLTrans::getString(const std::string &xml_desc, const LLStringUtil::format_map_t& msg_args)
+{
+ // Don't care about time as much as call count. Make sure we're not
+ // calling LLTrans::getString() in an inner loop. JC
+ LL_RECORD_BLOCK_TIME(FTM_GET_TRANS);
+
+ template_map_t::iterator iter = sStringTemplates.find(xml_desc);
+ if (iter != sStringTemplates.end())
+ {
+ std::string text = iter->second.mText;
+ LLStringUtil::format_map_t args = sDefaultArgs;
+ args.insert(msg_args.begin(), msg_args.end());
+ LLStringUtil::format(text, args);
+
+ return text;
+ }
+ else
+ {
+ LL_WARNS_ONCE("configuration") << "Missing String in strings.xml: [" << xml_desc << "]" << LL_ENDL;
+ return "MissingString("+xml_desc+")";
+ }
+}
+
+//static
+std::string LLTrans::getString(const std::string &xml_desc, const LLSD& msg_args)
+{
+ // Don't care about time as much as call count. Make sure we're not
+ // calling LLTrans::getString() in an inner loop. JC
+ LL_RECORD_BLOCK_TIME(FTM_GET_TRANS);
+
+ template_map_t::iterator iter = sStringTemplates.find(xml_desc);
+ if (iter != sStringTemplates.end())
+ {
+ std::string text = iter->second.mText;
+ LLStringUtil::format(text, msg_args);
+ return text;
+ }
+ else
+ {
+ LL_WARNS_ONCE("configuration") << "Missing String in strings.xml: [" << xml_desc << "]" << LL_ENDL;
+ return "MissingString("+xml_desc+")";
+ }
+}
+
+//static
+bool LLTrans::findString(std::string &result, const std::string &xml_desc, const LLStringUtil::format_map_t& msg_args)
+{
+ LL_RECORD_BLOCK_TIME(FTM_GET_TRANS);
+
+ template_map_t::iterator iter = sStringTemplates.find(xml_desc);
+ if (iter != sStringTemplates.end())
+ {
+ std::string text = iter->second.mText;
+ LLStringUtil::format_map_t args = sDefaultArgs;
+ args.insert(msg_args.begin(), msg_args.end());
+ LLStringUtil::format(text, args);
+ result = text;
+ return true;
+ }
+ else
+ {
+ LL_WARNS_ONCE("configuration") << "Missing String in strings.xml: [" << xml_desc << "]" << LL_ENDL;
+ return false;
+ }
+}
+
+//static
+bool LLTrans::findString(std::string &result, const std::string &xml_desc, const LLSD& msg_args)
+{
+ LL_RECORD_BLOCK_TIME(FTM_GET_TRANS);
+
+ template_map_t::iterator iter = sStringTemplates.find(xml_desc);
+ if (iter != sStringTemplates.end())
+ {
+ std::string text = iter->second.mText;
+ LLStringUtil::format(text, msg_args);
+ result = text;
+ return true;
+ }
+ else
+ {
+ LL_WARNS_ONCE("configuration") << "Missing String in strings.xml: [" << xml_desc << "]" << LL_ENDL;
+ return false;
+ }
+}
+
+//static
+std::string LLTrans::getCountString(const std::string& language, const std::string& xml_desc, S32 count)
+{
+ // Compute which string identifier to use
+ const char* form = "";
+ if (language == "ru") // Russian
+ {
+ // From GNU ngettext()
+ // Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;
+ if (count % 10 == 1
+ && count % 100 != 11)
+ {
+ // singular, "1 item"
+ form = "A";
+ }
+ else if (count % 10 >= 2
+ && count % 10 <= 4
+ && (count % 100 < 10 || count % 100 >= 20) )
+ {
+ // special case "2 items", "23 items", but not "13 items"
+ form = "B";
+ }
+ else
+ {
+ // English-style plural, "5 items"
+ form = "C";
+ }
+ }
+ else if (language == "fr" || language == "pt") // French, Brazilian Portuguese
+ {
+ // French and Portuguese treat zero as a singular "0 item" not "0 items"
+ if (count == 0 || count == 1)
+ {
+ form = "A";
+ }
+ else
+ {
+ // English-style plural
+ form = "B";
+ }
+ }
+ else // default
+ {
+ // languages like English with 2 forms, singular and plural
+ if (count == 1)
+ {
+ // "1 item"
+ form = "A";
+ }
+ else
+ {
+ // "2 items", also use plural for "0 items"
+ form = "B";
+ }
+ }
+
+ // Translate that string
+ LLStringUtil::format_map_t args;
+ args["[COUNT]"] = llformat("%d", count);
+
+ // Look up "AgeYearsB" or "AgeWeeksC" including the "form"
+ std::string key = llformat("%s%s", xml_desc.c_str(), form);
+ return getString(key, args);
+}
+
+void LLTrans::setDefaultArg(const std::string& name, const std::string& value)
+{
+ sDefaultArgs[name] = value;
+}
diff --git a/indra/llui/lltrans.h b/indra/llui/lltrans.h
new file mode 100755
index 0000000000..a47ce94f08
--- /dev/null
+++ b/indra/llui/lltrans.h
@@ -0,0 +1,134 @@
+/**
+ * @file lltrans.h
+ * @brief LLTrans definition
+ *
+ * $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_TRANS_H
+#define LL_TRANS_H
+
+#include <map>
+#include <set>
+
+#include "llpointer.h"
+#include "llstring.h"
+
+class LLXMLNode;
+
+class LLSD;
+
+/**
+ * @brief String template loaded from strings.xml
+ */
+class LLTransTemplate
+{
+public:
+ LLTransTemplate(const std::string& name = LLStringUtil::null, const std::string& text = LLStringUtil::null) : mName(name), mText(text) {}
+
+ std::string mName;
+ std::string mText;
+};
+
+/**
+ * @brief Localized strings class
+ * This class is used to retrieve translations of strings used to build larger ones, as well as
+ * strings with a general usage that don't belong to any specific floater. For example,
+ * "Owner:", "Retrieving..." used in the place of a not yet known name, etc.
+ */
+class LLTrans
+{
+public:
+ LLTrans();
+
+ /**
+ * @brief Parses the xml root that holds the strings. Used once on startup
+// *FIXME * @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
+ */
+ static bool parseStrings(LLPointer<LLXMLNode> & root, const std::set<std::string>& default_args);
+
+ static bool parseLanguageStrings(LLPointer<LLXMLNode> & root);
+
+ /**
+ * @brief Returns a translated string
+ * @param xml_desc String's description
+ * @param args A list of substrings to replace in the string
+ * @returns Translated string
+ */
+ static std::string getString(const std::string &xml_desc, const LLStringUtil::format_map_t& args);
+ static std::string getString(const std::string &xml_desc, const LLSD& args);
+ static bool findString(std::string &result, const std::string &xml_desc, const LLStringUtil::format_map_t& args);
+ static bool findString(std::string &result, const std::string &xml_desc, const LLSD& args);
+
+ // Returns translated string with [COUNT] replaced with a number, following
+ // special per-language logic for plural nouns. For example, some languages
+ // may have different plurals for 0, 1, 2 and > 2.
+ // See "AgeWeeksA", "AgeWeeksB", etc. in strings.xml for examples.
+ static std::string getCountString(const std::string& language, const std::string& xml_desc, S32 count);
+
+ /**
+ * @brief Returns a translated string
+ * @param xml_desc String's description
+ * @returns Translated string
+ */
+ static std::string getString(const std::string &xml_desc)
+ {
+ LLStringUtil::format_map_t empty;
+ return getString(xml_desc, empty);
+ }
+
+ static bool findString(std::string &result, const std::string &xml_desc)
+ {
+ LLStringUtil::format_map_t empty;
+ return findString(result, xml_desc, empty);
+ }
+
+ static std::string getKeyboardString(const char* keystring)
+ {
+ std::string key_str(keystring);
+ std::string trans_str;
+ return findString(trans_str, key_str) ? trans_str : key_str;
+ }
+
+ // get the default args
+ static const LLStringUtil::format_map_t& getDefaultArgs()
+ {
+ return sDefaultArgs;
+ }
+
+ static void setDefaultArg(const std::string& name, const std::string& value);
+
+ // insert default args into an arg list
+ static void getArgs(LLStringUtil::format_map_t& args)
+ {
+ args.insert(sDefaultArgs.begin(), sDefaultArgs.end());
+ }
+
+private:
+ typedef std::map<std::string, LLTransTemplate > template_map_t;
+ static template_map_t sStringTemplates;
+ static LLStringUtil::format_map_t sDefaultArgs;
+};
+
+#endif
diff --git a/indra/llui/lltransutil.cpp b/indra/llui/lltransutil.cpp
index 9d0ff9d5cb..220cee4c90 100644..100755
--- a/indra/llui/lltransutil.cpp
+++ b/indra/llui/lltransutil.cpp
@@ -26,19 +26,25 @@
#include "linden_common.h"
-#include "lltrans.h"
-#include "lluictrlfactory.h"
-
#include "lltransutil.h"
+#include "lltrans.h"
+#include "lluictrlfactory.h"
+#include "llxmlnode.h"
+#include "lldir.h"
bool LLTransUtil::parseStrings(const std::string& xml_filename, const std::set<std::string>& default_args)
{
LLXMLNodePtr root;
- BOOL success = LLUICtrlFactory::getLayeredXMLNode(xml_filename, root);
+ // Pass LLDir::ALL_SKINS to load a composite of all the individual string
+ // definitions in the default skin and the current skin. This means an
+ // individual skin can provide an xml_filename that overrides only a
+ // subset of the available string definitions; any string definition not
+ // overridden by that skin will be sought in the default skin.
+ bool success = LLUICtrlFactory::getLayeredXMLNode(xml_filename, root, LLDir::ALL_SKINS);
if (!success)
{
- llerrs << "Couldn't load string table" << llendl;
+ LL_ERRS() << "Couldn't load string table " << xml_filename << LL_ENDL;
return false;
}
@@ -53,7 +59,7 @@ bool LLTransUtil::parseLanguageStrings(const std::string& xml_filename)
if (!success)
{
- llerrs << "Couldn't load string table " << xml_filename << llendl;
+ LL_ERRS() << "Couldn't load localization table " << xml_filename << LL_ENDL;
return false;
}
diff --git a/indra/llui/lltransutil.h b/indra/llui/lltransutil.h
index 9c7cee3f6f..9c7cee3f6f 100644..100755
--- a/indra/llui/lltransutil.h
+++ b/indra/llui/lltransutil.h
diff --git a/indra/llui/llui.cpp b/indra/llui/llui.cpp
index c300fe55d9..aabc7ed2e4 100644..100755
--- a/indra/llui/llui.cpp
+++ b/indra/llui/llui.cpp
@@ -39,8 +39,10 @@
#include "llrect.h"
#include "lldir.h"
#include "llgl.h"
+#include "llsd.h"
// Project includes
+#include "llcommandmanager.h"
#include "llcontrol.h"
#include "llui.h"
#include "lluicolortable.h"
@@ -57,23 +59,24 @@
#include "llfiltereditor.h"
#include "llflyoutbutton.h"
#include "llsearcheditor.h"
+#include "lltoolbar.h"
// for XUIParse
#include "llquaternion.h"
#include <boost/tokenizer.hpp>
+#include <boost/algorithm/string/find_iterator.hpp>
+#include <boost/algorithm/string/finder.hpp>
//
// Globals
//
-const LLColor4 UI_VERTEX_COLOR(1.f, 1.f, 1.f, 1.f);
// Language for UI construction
std::map<std::string, std::string> gTranslation;
std::list<std::string> gUntranslated;
/*static*/ LLUI::settings_map_t LLUI::sSettingGroups;
-/*static*/ LLImageProviderInterface* LLUI::sImageProvider = NULL;
/*static*/ LLUIAudioCallback LLUI::sAudioCallback = NULL;
-/*static*/ LLVector2 LLUI::sGLScaleFactor(1.f, 1.f);
+/*static*/ LLUIAudioCallback LLUI::sDeferredAudioCallback = NULL;
/*static*/ LLWindow* LLUI::sWindow = NULL;
/*static*/ LLView* LLUI::sRootView = NULL;
/*static*/ BOOL LLUI::sDirty = FALSE;
@@ -85,1529 +88,108 @@ std::list<std::string> gUntranslated;
/*static*/ LLUI::remove_popup_t LLUI::sRemovePopupFunc;
/*static*/ LLUI::clear_popups_t LLUI::sClearPopupsFunc;
-// register filtereditor here
+// register filter editor 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");
-
+static LLDefaultChildRegistry::Register<LLToolBar> register_toolbar("toolbar");
//
// Functions
//
-void make_ui_sound(const char* namep)
+
+LLUUID find_ui_sound(const char * namep)
{
std::string name = ll_safe_string(namep);
+ LLUUID uuid = LLUUID(NULL);
if (!LLUI::sSettingGroups["config"]->controlExists(name))
{
- llwarns << "tried to make ui sound for unknown sound name: " << name << llendl;
+ LL_WARNS() << "tried to make UI sound for unknown sound name: " << name << LL_ENDL;
}
else
{
- LLUUID uuid(LLUI::sSettingGroups["config"]->getString(name));
+ uuid = LLUUID(LLUI::sSettingGroups["config"]->getString(name));
if (uuid.isNull())
{
if (LLUI::sSettingGroups["config"]->getString(name) == LLUUID::null.asString())
{
if (LLUI::sSettingGroups["config"]->getBOOL("UISndDebugSpamToggle"))
{
- llinfos << "ui sound name: " << name << " triggered but silent (null uuid)" << llendl;
+ LL_INFOS() << "UI sound name: " << name << " triggered but silent (null uuid)" << LL_ENDL;
}
}
else
{
- llwarns << "ui sound named: " << name << " does not translate to a valid uuid" << llendl;
+ LL_WARNS() << "UI sound named: " << name << " does not translate to a valid uuid" << LL_ENDL;
}
-
}
else if (LLUI::sAudioCallback != NULL)
{
if (LLUI::sSettingGroups["config"]->getBOOL("UISndDebugSpamToggle"))
{
- llinfos << "ui sound name: " << name << llendl;
+ LL_INFOS() << "UI sound name: " << name << LL_ENDL;
}
- LLUI::sAudioCallback(uuid);
- }
- }
-}
-
-BOOL ui_point_in_rect(S32 x, S32 y, S32 left, S32 top, S32 right, S32 bottom)
-{
- if (x < left || right < x) return FALSE;
- if (y < bottom || top < y) return FALSE;
- return TRUE;
-}
-
-
-// Puts GL into 2D drawing mode by turning off lighting, setting to an
-// orthographic projection, etc.
-void gl_state_for_2d(S32 width, S32 height)
-{
- stop_glerror();
- F32 window_width = (F32) width;//gViewerWindow->getWindowWidth();
- F32 window_height = (F32) height;//gViewerWindow->getWindowHeight();
-
- glMatrixMode(GL_PROJECTION);
- glLoadIdentity();
- glOrtho(0.0f, llmax(window_width, 1.f), 0.0f, llmax(window_height,1.f), -1.0f, 1.0f);
- glMatrixMode(GL_MODELVIEW);
- glLoadIdentity();
- stop_glerror();
-}
-
-
-void gl_draw_x(const LLRect& rect, const LLColor4& color)
-{
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-
- gGL.color4fv( color.mV );
-
- gGL.begin( LLRender::LINES );
- gGL.vertex2i( rect.mLeft, rect.mTop );
- gGL.vertex2i( rect.mRight, rect.mBottom );
- gGL.vertex2i( rect.mLeft, rect.mBottom );
- gGL.vertex2i( rect.mRight, rect.mTop );
- gGL.end();
-}
-
-
-void gl_rect_2d_offset_local( S32 left, S32 top, S32 right, S32 bottom, const LLColor4 &color, S32 pixel_offset, BOOL filled)
-{
- gGL.color4fv(color.mV);
- gl_rect_2d_offset_local(left, top, right, bottom, pixel_offset, filled);
-}
-
-void gl_rect_2d_offset_local( S32 left, S32 top, S32 right, S32 bottom, S32 pixel_offset, BOOL filled)
-{
- gGL.pushUIMatrix();
- left += LLFontGL::sCurOrigin.mX;
- right += LLFontGL::sCurOrigin.mX;
- bottom += LLFontGL::sCurOrigin.mY;
- top += LLFontGL::sCurOrigin.mY;
-
- 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.popUIMatrix();
-}
-
-
-void gl_rect_2d(S32 left, S32 top, S32 right, S32 bottom, BOOL filled )
-{
- stop_glerror();
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-
- // Counterclockwise quad will face the viewer
- if( filled )
- {
- gGL.begin( LLRender::QUADS );
- gGL.vertex2i(left, top);
- gGL.vertex2i(left, bottom);
- gGL.vertex2i(right, bottom);
- gGL.vertex2i(right, top);
- gGL.end();
- }
- else
- {
- if( gGLManager.mATIOffsetVerticalLines )
- {
- // Work around bug in ATI driver: vertical lines are offset by (-1,-1)
- gGL.begin( LLRender::LINES );
-
- // Verticals
- gGL.vertex2i(left + 1, top);
- gGL.vertex2i(left + 1, bottom);
-
- gGL.vertex2i(right, bottom);
- gGL.vertex2i(right, top);
-
- // Horizontals
- top--;
- right--;
- gGL.vertex2i(left, bottom);
- gGL.vertex2i(right, bottom);
-
- gGL.vertex2i(left, top);
- gGL.vertex2i(right, top);
- gGL.end();
- }
- else
- {
- top--;
- right--;
- gGL.begin( LLRender::LINE_STRIP );
- gGL.vertex2i(left, top);
- gGL.vertex2i(left, bottom);
- gGL.vertex2i(right, bottom);
- gGL.vertex2i(right, top);
- gGL.vertex2i(left, top);
- gGL.end();
- }
- }
- stop_glerror();
-}
-
-void gl_rect_2d(S32 left, S32 top, S32 right, S32 bottom, const LLColor4 &color, BOOL filled )
-{
- gGL.color4fv( color.mV );
- gl_rect_2d( left, top, right, bottom, filled );
-}
-
-
-void gl_rect_2d( const LLRect& rect, const LLColor4& color, BOOL filled )
-{
- gGL.color4fv( color.mV );
- gl_rect_2d( rect.mLeft, rect.mTop, rect.mRight, rect.mBottom, filled );
-}
-
-// Given a rectangle on the screen, draws a drop shadow _outside_
-// the right and bottom edges of it. Along the right it has width "lines"
-// and along the bottom it has height "lines".
-void gl_drop_shadow(S32 left, S32 top, S32 right, S32 bottom, const LLColor4 &start_color, S32 lines)
-{
- stop_glerror();
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-
- // HACK: Overlap with the rectangle by a single pixel.
- right--;
- bottom++;
- lines++;
-
- LLColor4 end_color = start_color;
- end_color.mV[VALPHA] = 0.f;
-
- gGL.begin(LLRender::QUADS);
-
- // Right edge, CCW faces screen
- gGL.color4fv(start_color.mV);
- gGL.vertex2i(right, top-lines);
- gGL.vertex2i(right, bottom);
- gGL.color4fv(end_color.mV);
- gGL.vertex2i(right+lines, bottom);
- gGL.vertex2i(right+lines, top-lines);
-
- // Bottom edge, CCW faces screen
- gGL.color4fv(start_color.mV);
- gGL.vertex2i(right, bottom);
- gGL.vertex2i(left+lines, bottom);
- gGL.color4fv(end_color.mV);
- gGL.vertex2i(left+lines, bottom-lines);
- gGL.vertex2i(right, bottom-lines);
-
- // bottom left Corner
- gGL.color4fv(start_color.mV);
- gGL.vertex2i(left+lines, bottom);
- gGL.color4fv(end_color.mV);
- gGL.vertex2i(left, bottom);
- // make the bottom left corner not sharp
- gGL.vertex2i(left+1, bottom-lines+1);
- gGL.vertex2i(left+lines, bottom-lines);
-
- // bottom right corner
- gGL.color4fv(start_color.mV);
- gGL.vertex2i(right, bottom);
- gGL.color4fv(end_color.mV);
- gGL.vertex2i(right, bottom-lines);
- // make the rightmost corner not sharp
- gGL.vertex2i(right+lines-1, bottom-lines+1);
- gGL.vertex2i(right+lines, bottom);
-
- // top right corner
- gGL.color4fv(start_color.mV);
- gGL.vertex2i( right, top-lines );
- gGL.color4fv(end_color.mV);
- gGL.vertex2i( right+lines, top-lines );
- // make the corner not sharp
- gGL.vertex2i( right+lines-1, top-1 );
- gGL.vertex2i( right, top );
-
- gGL.end();
- stop_glerror();
-}
-
-void gl_line_2d(S32 x1, S32 y1, S32 x2, S32 y2 )
-{
- // Work around bug in ATI driver: vertical lines are offset by (-1,-1)
- if( (x1 == x2) && gGLManager.mATIOffsetVerticalLines )
- {
- x1++;
- x2++;
- y1++;
- y2++;
- }
-
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-
- gGL.begin(LLRender::LINES);
- gGL.vertex2i(x1, y1);
- gGL.vertex2i(x2, y2);
- gGL.end();
-}
-
-void gl_line_2d(S32 x1, S32 y1, S32 x2, S32 y2, const LLColor4 &color )
-{
- // Work around bug in ATI driver: vertical lines are offset by (-1,-1)
- if( (x1 == x2) && gGLManager.mATIOffsetVerticalLines )
- {
- x1++;
- x2++;
- y1++;
- y2++;
- }
-
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-
- gGL.color4fv( color.mV );
-
- gGL.begin(LLRender::LINES);
- gGL.vertex2i(x1, y1);
- gGL.vertex2i(x2, y2);
- gGL.end();
-}
-
-void gl_triangle_2d(S32 x1, S32 y1, S32 x2, S32 y2, S32 x3, S32 y3, const LLColor4& color, BOOL filled)
-{
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-
- gGL.color4fv(color.mV);
-
- if (filled)
- {
- gGL.begin(LLRender::TRIANGLES);
- }
- else
- {
- gGL.begin(LLRender::LINE_LOOP);
- }
- gGL.vertex2i(x1, y1);
- gGL.vertex2i(x2, y2);
- gGL.vertex2i(x3, y3);
- gGL.end();
-}
-
-void gl_corners_2d(S32 left, S32 top, S32 right, S32 bottom, S32 length, F32 max_frac)
-{
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-
- length = llmin((S32)(max_frac*(right - left)), length);
- length = llmin((S32)(max_frac*(top - bottom)), length);
- gGL.begin(LLRender::LINES);
- gGL.vertex2i(left, top);
- gGL.vertex2i(left + length, top);
-
- gGL.vertex2i(left, top);
- gGL.vertex2i(left, top - length);
-
- gGL.vertex2i(left, bottom);
- gGL.vertex2i(left + length, bottom);
-
- gGL.vertex2i(left, bottom);
- gGL.vertex2i(left, bottom + length);
-
- gGL.vertex2i(right, top);
- gGL.vertex2i(right - length, top);
-
- gGL.vertex2i(right, top);
- gGL.vertex2i(right, top - length);
-
- gGL.vertex2i(right, bottom);
- gGL.vertex2i(right - length, bottom);
-
- gGL.vertex2i(right, bottom);
- gGL.vertex2i(right, bottom + length);
- gGL.end();
-}
-
-
-void gl_draw_image( S32 x, S32 y, LLTexture* image, const LLColor4& color, const LLRectf& uv_rect )
-{
- if (NULL == image)
- {
- llwarns << "image == NULL; aborting function" << llendl;
- return;
- }
- 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, LLTexture* image, const LLColor4& color, const LLRectf& uv_rect)
-{
- if (NULL == image)
- {
- llwarns << "image == NULL; aborting function" << llendl;
- return;
- }
- 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, LLTexture* image, const LLColor4& color, BOOL solid_color, const LLRectf& uv_rect)
-{
- if (NULL == image)
- {
- llwarns << "image == NULL; aborting function" << llendl;
- return;
- }
-
- // scale screen size of borders down
- F32 border_width_fraction = (F32)border_width / (F32)image->getWidth(0);
- F32 border_height_fraction = (F32)border_height / (F32)image->getHeight(0);
-
- LLRectf scale_rect(border_width_fraction, 1.f - border_height_fraction, 1.f - border_width_fraction, border_height_fraction);
- 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, LLTexture* image, const LLColor4& color, BOOL solid_color, const LLRectf& uv_outer_rect, const LLRectf& center_rect)
-{
- stop_glerror();
-
- if (NULL == image)
- {
- llwarns << "image == NULL; aborting function" << llendl;
- 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 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;
-
- if (solid_color)
- {
- gGL.getTexUnit(0)->setTextureColorBlend(LLTexUnit::TBO_REPLACE, LLTexUnit::TBS_PREV_COLOR);
- gGL.getTexUnit(0)->setTextureAlphaBlend(LLTexUnit::TBO_MULT, LLTexUnit::TBS_TEX_ALPHA, LLTexUnit::TBS_VERT_ALPHA);
- }
-
- 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];
-
- S32 index = 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++;
-
- 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++;
-
- 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++;
-
- 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++;
-
- // 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++;
-
- 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++;
-
- 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++;
-
- 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 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++;
-
- 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++;
-
- 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++;
-
- 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 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++;
-
- 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++;
-
- 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++;
-
- 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++;
-
- // 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++;
-
- 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++;
-
- 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++;
-
- 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++;
-
- 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++;
-
- 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++;
-
- 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
- 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++;
-
- 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++;
-
- 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++;
-
- 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
- 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++;
-
- 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++;
-
- 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++;
-
- 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
- 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++;
-
- 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++;
-
- 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++;
-
- 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.end();
-
- if (solid_color)
- {
- gGL.getTexUnit(0)->setTextureBlendType(LLTexUnit::TB_MULT);
- }
-}
-
-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, LLTexture* image, const LLColor4& color, const LLRectf& uv_rect)
-{
- if (NULL == image)
- {
- llwarns << "image == NULL; aborting function" << llendl;
- return;
- }
-
- LLGLSUIDefault gls_ui;
-
-
- gGL.getTexUnit(0)->bind(image);
-
- gGL.color4fv(color.mV);
-
- if (degrees == 0.f)
- {
- const S32 NUM_VERTICES = 4; // 9 quads
- LLVector2 uv[NUM_VERTICES];
- LLVector3 pos[NUM_VERTICES];
-
- 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();
- }
- else
- {
- gGL.pushUIMatrix();
- gGL.translateUI((F32)x, (F32)y, 0.f);
-
- F32 offset_x = F32(width/2);
- F32 offset_y = F32(height/2);
-
- gGL.translateUI(offset_x, offset_y, 0.f);
-
- LLMatrix3 quat(0.f, 0.f, degrees*DEG_TO_RAD);
-
- gGL.getTexUnit(0)->bind(image);
-
- gGL.color4fv(color.mV);
-
- gGL.begin(LLRender::QUADS);
- {
- LLVector3 v;
-
- v = LLVector3(offset_x, offset_y, 0.f) * quat;
- gGL.texCoord2f(uv_rect.mRight, uv_rect.mTop);
- gGL.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.vertex2f(v.mV[0], v.mV[1] );
-
- v = LLVector3(-offset_x, -offset_y, 0.f) * quat;
- gGL.texCoord2f(uv_rect.mLeft, uv_rect.mBottom);
- gGL.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();
- }
-}
-
-
-void gl_stippled_line_3d( const LLVector3& start, const LLVector3& end, const LLColor4& color, F32 phase )
-{
- phase = fmod(phase, 1.f);
-
- S32 shift = S32(phase * 4.f) % 4;
-
- // Stippled line
- LLGLEnable stipple(GL_LINE_STIPPLE);
-
- gGL.color4f(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE], color.mV[VALPHA]);
-
- gGL.flush();
- glLineWidth(2.5f);
- glLineStipple(2, 0x3333 << shift);
-
- gGL.begin(LLRender::LINES);
- {
- gGL.vertex3fv( start.mV );
- gGL.vertex3fv( end.mV );
- }
- gGL.end();
-
- LLUI::setLineWidth(1.f);
-}
-
-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)
- {
- end_angle += F_TWO_PI;
- }
-
- gGL.pushUIMatrix();
- {
- gGL.translateUI(center_x, center_y, 0.f);
-
- // Inexact, but reasonably fast.
- F32 delta = (end_angle - start_angle) / steps;
- F32 sin_delta = sin( delta );
- F32 cos_delta = cos( delta );
- F32 x = cosf(start_angle) * radius;
- F32 y = sinf(start_angle) * radius;
-
- if (filled)
- {
- gGL.begin(LLRender::TRIANGLE_FAN);
- gGL.vertex2f(0.f, 0.f);
- // make sure circle is complete
- steps += 1;
- }
- else
- {
- gGL.begin(LLRender::LINE_STRIP);
- }
-
- while( steps-- )
- {
- // Successive rotations
- gGL.vertex2f( x, y );
- F32 x_new = x * cos_delta - y * sin_delta;
- y = x * sin_delta + y * cos_delta;
- x = x_new;
- }
- gGL.end();
- }
- gGL.popUIMatrix();
-}
-
-void gl_circle_2d(F32 center_x, F32 center_y, F32 radius, S32 steps, BOOL filled)
-{
- gGL.pushUIMatrix();
- {
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
- gGL.translateUI(center_x, center_y, 0.f);
-
- // Inexact, but reasonably fast.
- F32 delta = F_TWO_PI / steps;
- F32 sin_delta = sin( delta );
- F32 cos_delta = cos( delta );
- F32 x = radius;
- F32 y = 0.f;
-
- if (filled)
- {
- gGL.begin(LLRender::TRIANGLE_FAN);
- gGL.vertex2f(0.f, 0.f);
- // make sure circle is complete
- steps += 1;
- }
- else
- {
- gGL.begin(LLRender::LINE_LOOP);
- }
-
- while( steps-- )
- {
- // Successive rotations
- gGL.vertex2f( x, y );
- F32 x_new = x * cos_delta - y * sin_delta;
- y = x * sin_delta + y * cos_delta;
- x = x_new;
- }
- gGL.end();
- }
- gGL.popUIMatrix();
-}
-
-// Renders a ring with sides (tube shape)
-void gl_deep_circle( F32 radius, F32 depth, S32 steps )
-{
- F32 x = radius;
- F32 y = 0.f;
- F32 angle_delta = F_TWO_PI / (F32)steps;
- gGL.begin( LLRender::TRIANGLE_STRIP );
- {
- S32 step = steps + 1; // An extra step to close the circle.
- while( step-- )
- {
- gGL.vertex3f( x, y, depth );
- gGL.vertex3f( x, y, 0.f );
-
- F32 x_new = x * cosf(angle_delta) - y * sinf(angle_delta);
- y = x * sinf(angle_delta) + y * cosf(angle_delta);
- x = x_new;
- }
- }
- gGL.end();
-}
-
-void gl_ring( F32 radius, F32 width, const LLColor4& center_color, const LLColor4& side_color, S32 steps, BOOL render_center )
-{
- gGL.pushUIMatrix();
- {
- gGL.translateUI(0.f, 0.f, -width / 2);
- if( render_center )
- {
- gGL.color4fv(center_color.mV);
- gl_deep_circle( radius, width, steps );
- }
- else
- {
- gl_washer_2d(radius, radius - width, steps, side_color, side_color);
- gGL.translateUI(0.f, 0.f, width);
- gl_washer_2d(radius - width, radius, steps, side_color, side_color);
- }
- }
- gGL.popUIMatrix();
-}
-
-// Draw gray and white checkerboard with black border
-void gl_rect_2d_checkerboard(const LLRect& rect, GLfloat alpha)
-{
- // Initialize the first time this is called.
- const S32 PIXELS = 32;
- static GLubyte checkerboard[PIXELS * PIXELS];
- static BOOL first = TRUE;
- if( first )
- {
- for( S32 i = 0; i < PIXELS; i++ )
- {
- for( S32 j = 0; j < PIXELS; j++ )
- {
- checkerboard[i * PIXELS + j] = ((i & 1) ^ (j & 1)) * 0xFF;
- }
- }
- first = FALSE;
- }
-
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-
- // ...white squares
- gGL.color4f( 1.f, 1.f, 1.f, alpha );
- gl_rect_2d(rect);
-
- // ...gray squares
- gGL.color4f( .7f, .7f, .7f, alpha );
- gGL.flush();
- glPolygonStipple( checkerboard );
-
- LLGLEnable polygon_stipple(GL_POLYGON_STIPPLE);
- gl_rect_2d(rect);
- gGL.flush();
-}
-
-
-// Draws the area between two concentric circles, like
-// a doughnut or washer.
-void gl_washer_2d(F32 outer_radius, F32 inner_radius, S32 steps, const LLColor4& inner_color, const LLColor4& outer_color)
-{
- const F32 DELTA = F_TWO_PI / steps;
- const F32 SIN_DELTA = sin( DELTA );
- const F32 COS_DELTA = cos( DELTA );
-
- F32 x1 = outer_radius;
- F32 y1 = 0.f;
- F32 x2 = inner_radius;
- F32 y2 = 0.f;
-
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-
- gGL.begin( LLRender::TRIANGLE_STRIP );
- {
- steps += 1; // An extra step to close the circle.
- while( steps-- )
- {
- 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();
-}
-
-// Draws the area between two concentric circles, like
-// a doughnut or washer.
-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)
-{
- const F32 DELTA = (end_radians - start_radians) / steps;
- const F32 SIN_DELTA = sin( DELTA );
- const F32 COS_DELTA = cos( DELTA );
-
- F32 x1 = outer_radius * cos( start_radians );
- F32 y1 = outer_radius * sin( start_radians );
- F32 x2 = inner_radius * cos( start_radians );
- F32 y2 = inner_radius * sin( start_radians );
-
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
- gGL.begin( LLRender::TRIANGLE_STRIP );
- {
- steps += 1; // An extra step to close the circle.
- while( steps-- )
- {
- 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 );
-
- gGL.texCoord2f(1.f, 1.f);
- gGL.vertex2i(width, height);
- gGL.texCoord2f(0.f, 1.f);
- gGL.vertex2i(0, height);
-
- gGL.texCoord2f(0.f, 0.f);
- gGL.vertex2i(0, 0);
-
- gGL.texCoord2f(1.f, 0.f);
- gGL.vertex2i(width, 0);
-
- gGL.end();
-}
-
-void gl_rect_2d_simple( S32 width, S32 height )
-{
- gGL.begin( LLRender::QUADS );
- gGL.vertex2i(width, height);
- gGL.vertex2i(0, height);
- gGL.vertex2i(0, 0);
- gGL.vertex2i(width, 0);
- gGL.end();
+ return uuid;
}
-void gl_segmented_rect_2d_tex(const S32 left,
- const S32 top,
- const S32 right,
- const S32 bottom,
- const S32 texture_width,
- const S32 texture_height,
- const S32 border_size,
- const U32 edges)
+void make_ui_sound(const char* namep)
{
- S32 width = llabs(right - left);
- S32 height = llabs(top - bottom);
-
- gGL.pushUIMatrix();
-
- 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)
- {
- border_uv_scale *= 0.5f / border_uv_scale.mV[VX];
- }
- if (border_uv_scale.mV[VY] > 0.5f)
- {
- border_uv_scale *= 0.5f / border_uv_scale.mV[VY];
- }
-
- F32 border_scale = llmin((F32)border_size, (F32)width * 0.5f, (F32)height * 0.5f);
- LLVector2 border_width_left = ((edges & (~(U32)ROUNDED_RECT_RIGHT)) != 0) ? LLVector2(border_scale, 0.f) : LLVector2::zero;
- LLVector2 border_width_right = ((edges & (~(U32)ROUNDED_RECT_LEFT)) != 0) ? LLVector2(border_scale, 0.f) : LLVector2::zero;
- LLVector2 border_height_bottom = ((edges & (~(U32)ROUNDED_RECT_TOP)) != 0) ? LLVector2(0.f, border_scale) : LLVector2::zero;
- LLVector2 border_height_top = ((edges & (~(U32)ROUNDED_RECT_BOTTOM)) != 0) ? LLVector2(0.f, border_scale) : LLVector2::zero;
- LLVector2 width_vec((F32)width, 0.f);
- LLVector2 height_vec(0.f, (F32)height);
-
- gGL.begin(LLRender::QUADS);
+ LLUUID soundUUID = find_ui_sound(namep);
+ if(soundUUID.notNull())
{
- // draw bottom left
- gGL.texCoord2f(0.f, 0.f);
- gGL.vertex2f(0.f, 0.f);
-
- gGL.texCoord2f(border_uv_scale.mV[VX], 0.f);
- gGL.vertex2fv(border_width_left.mV);
-
- gGL.texCoord2f(border_uv_scale.mV[VX], border_uv_scale.mV[VY]);
- gGL.vertex2fv((border_width_left + border_height_bottom).mV);
-
- gGL.texCoord2f(0.f, border_uv_scale.mV[VY]);
- gGL.vertex2fv(border_height_bottom.mV);
-
- // draw bottom middle
- gGL.texCoord2f(border_uv_scale.mV[VX], 0.f);
- gGL.vertex2fv(border_width_left.mV);
-
- gGL.texCoord2f(1.f - border_uv_scale.mV[VX], 0.f);
- gGL.vertex2fv((width_vec - border_width_right).mV);
-
- gGL.texCoord2f(1.f - border_uv_scale.mV[VX], border_uv_scale.mV[VY]);
- gGL.vertex2fv((width_vec - border_width_right + border_height_bottom).mV);
-
- gGL.texCoord2f(border_uv_scale.mV[VX], border_uv_scale.mV[VY]);
- gGL.vertex2fv((border_width_left + border_height_bottom).mV);
-
- // draw bottom right
- gGL.texCoord2f(1.f - border_uv_scale.mV[VX], 0.f);
- gGL.vertex2fv((width_vec - border_width_right).mV);
-
- gGL.texCoord2f(1.f, 0.f);
- gGL.vertex2fv(width_vec.mV);
-
- gGL.texCoord2f(1.f, border_uv_scale.mV[VY]);
- gGL.vertex2fv((width_vec + border_height_bottom).mV);
-
- gGL.texCoord2f(1.f - border_uv_scale.mV[VX], border_uv_scale.mV[VY]);
- gGL.vertex2fv((width_vec - border_width_right + border_height_bottom).mV);
-
- // draw left
- gGL.texCoord2f(0.f, border_uv_scale.mV[VY]);
- gGL.vertex2fv(border_height_bottom.mV);
-
- gGL.texCoord2f(border_uv_scale.mV[VX], border_uv_scale.mV[VY]);
- gGL.vertex2fv((border_width_left + border_height_bottom).mV);
-
- gGL.texCoord2f(border_uv_scale.mV[VX], 1.f - border_uv_scale.mV[VY]);
- gGL.vertex2fv((border_width_left + height_vec - border_height_top).mV);
-
- gGL.texCoord2f(0.f, 1.f - border_uv_scale.mV[VY]);
- gGL.vertex2fv((height_vec - border_height_top).mV);
-
- // draw middle
- gGL.texCoord2f(border_uv_scale.mV[VX], border_uv_scale.mV[VY]);
- gGL.vertex2fv((border_width_left + border_height_bottom).mV);
-
- gGL.texCoord2f(1.f - border_uv_scale.mV[VX], border_uv_scale.mV[VY]);
- gGL.vertex2fv((width_vec - border_width_right + border_height_bottom).mV);
-
- gGL.texCoord2f(1.f - border_uv_scale.mV[VX], 1.f - border_uv_scale.mV[VY]);
- gGL.vertex2fv((width_vec - border_width_right + height_vec - border_height_top).mV);
-
- gGL.texCoord2f(border_uv_scale.mV[VX], 1.f - border_uv_scale.mV[VY]);
- gGL.vertex2fv((border_width_left + height_vec - border_height_top).mV);
-
- // draw right
- gGL.texCoord2f(1.f - border_uv_scale.mV[VX], border_uv_scale.mV[VY]);
- gGL.vertex2fv((width_vec - border_width_right + border_height_bottom).mV);
-
- gGL.texCoord2f(1.f, border_uv_scale.mV[VY]);
- gGL.vertex2fv((width_vec + border_height_bottom).mV);
-
- gGL.texCoord2f(1.f, 1.f - border_uv_scale.mV[VY]);
- gGL.vertex2fv((width_vec + height_vec - border_height_top).mV);
-
- gGL.texCoord2f(1.f - border_uv_scale.mV[VX], 1.f - border_uv_scale.mV[VY]);
- gGL.vertex2fv((width_vec - border_width_right + height_vec - border_height_top).mV);
-
- // draw top left
- gGL.texCoord2f(0.f, 1.f - border_uv_scale.mV[VY]);
- gGL.vertex2fv((height_vec - border_height_top).mV);
-
- gGL.texCoord2f(border_uv_scale.mV[VX], 1.f - border_uv_scale.mV[VY]);
- gGL.vertex2fv((border_width_left + height_vec - border_height_top).mV);
-
- gGL.texCoord2f(border_uv_scale.mV[VX], 1.f);
- gGL.vertex2fv((border_width_left + height_vec).mV);
-
- gGL.texCoord2f(0.f, 1.f);
- gGL.vertex2fv((height_vec).mV);
-
- // draw top middle
- gGL.texCoord2f(border_uv_scale.mV[VX], 1.f - border_uv_scale.mV[VY]);
- gGL.vertex2fv((border_width_left + height_vec - border_height_top).mV);
-
- gGL.texCoord2f(1.f - border_uv_scale.mV[VX], 1.f - border_uv_scale.mV[VY]);
- gGL.vertex2fv((width_vec - border_width_right + height_vec - border_height_top).mV);
-
- gGL.texCoord2f(1.f - border_uv_scale.mV[VX], 1.f);
- gGL.vertex2fv((width_vec - border_width_right + height_vec).mV);
-
- gGL.texCoord2f(border_uv_scale.mV[VX], 1.f);
- gGL.vertex2fv((border_width_left + height_vec).mV);
-
- // draw top right
- gGL.texCoord2f(1.f - border_uv_scale.mV[VX], 1.f - border_uv_scale.mV[VY]);
- gGL.vertex2fv((width_vec - border_width_right + height_vec - border_height_top).mV);
-
- gGL.texCoord2f(1.f, 1.f - border_uv_scale.mV[VY]);
- gGL.vertex2fv((width_vec + height_vec - border_height_top).mV);
-
- gGL.texCoord2f(1.f, 1.f);
- gGL.vertex2fv((width_vec + height_vec).mV);
-
- gGL.texCoord2f(1.f - border_uv_scale.mV[VX], 1.f);
- gGL.vertex2fv((width_vec - border_width_right + height_vec).mV);
+ LLUI::sAudioCallback(soundUUID);
}
- gGL.end();
-
- gGL.popUIMatrix();
}
-//FIXME: rewrite to use scissor?
-void gl_segmented_rect_2d_fragment_tex(const S32 left,
- const S32 top,
- const S32 right,
- const S32 bottom,
- const S32 texture_width,
- const S32 texture_height,
- const S32 border_size,
- const F32 start_fragment,
- const F32 end_fragment,
- const U32 edges)
+void make_ui_sound_deferred(const char* namep)
{
- S32 width = llabs(right - left);
- S32 height = llabs(top - bottom);
-
- gGL.pushUIMatrix();
-
- 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)
- {
- border_uv_scale *= 0.5f / border_uv_scale.mV[VX];
- }
- if (border_uv_scale.mV[VY] > 0.5f)
+ LLUUID soundUUID = find_ui_sound(namep);
+ if(soundUUID.notNull())
{
- border_uv_scale *= 0.5f / border_uv_scale.mV[VY];
+ LLUI::sDeferredAudioCallback(soundUUID);
}
-
- F32 border_scale = llmin((F32)border_size, (F32)width * 0.5f, (F32)height * 0.5f);
- LLVector2 border_width_left = ((edges & (~(U32)ROUNDED_RECT_RIGHT)) != 0) ? LLVector2(border_scale, 0.f) : LLVector2::zero;
- LLVector2 border_width_right = ((edges & (~(U32)ROUNDED_RECT_LEFT)) != 0) ? LLVector2(border_scale, 0.f) : LLVector2::zero;
- LLVector2 border_height_bottom = ((edges & (~(U32)ROUNDED_RECT_TOP)) != 0) ? LLVector2(0.f, border_scale) : LLVector2::zero;
- LLVector2 border_height_top = ((edges & (~(U32)ROUNDED_RECT_BOTTOM)) != 0) ? LLVector2(0.f, border_scale) : LLVector2::zero;
- LLVector2 width_vec((F32)width, 0.f);
- LLVector2 height_vec(0.f, (F32)height);
-
- F32 middle_start = border_scale / (F32)width;
- F32 middle_end = 1.f - middle_start;
-
- F32 u_min;
- F32 u_max;
- LLVector2 x_min;
- LLVector2 x_max;
-
- gGL.begin(LLRender::QUADS);
- {
- if (start_fragment < middle_start)
- {
- u_min = (start_fragment / middle_start) * border_uv_scale.mV[VX];
- u_max = llmin(end_fragment / middle_start, 1.f) * border_uv_scale.mV[VX];
- x_min = (start_fragment / middle_start) * border_width_left;
- x_max = llmin(end_fragment / middle_start, 1.f) * border_width_left;
-
- // draw bottom left
- gGL.texCoord2f(u_min, 0.f);
- gGL.vertex2fv(x_min.mV);
-
- gGL.texCoord2f(border_uv_scale.mV[VX], 0.f);
- gGL.vertex2fv(x_max.mV);
-
- gGL.texCoord2f(u_max, border_uv_scale.mV[VY]);
- gGL.vertex2fv((x_max + border_height_bottom).mV);
-
- gGL.texCoord2f(u_min, border_uv_scale.mV[VY]);
- gGL.vertex2fv((x_min + border_height_bottom).mV);
-
- // draw left
- gGL.texCoord2f(u_min, border_uv_scale.mV[VY]);
- gGL.vertex2fv((x_min + border_height_bottom).mV);
-
- gGL.texCoord2f(u_max, border_uv_scale.mV[VY]);
- gGL.vertex2fv((x_max + border_height_bottom).mV);
-
- gGL.texCoord2f(u_max, 1.f - border_uv_scale.mV[VY]);
- gGL.vertex2fv((x_max + height_vec - border_height_top).mV);
-
- gGL.texCoord2f(u_min, 1.f - border_uv_scale.mV[VY]);
- gGL.vertex2fv((x_min + height_vec - border_height_top).mV);
-
- // draw top left
- gGL.texCoord2f(u_min, 1.f - border_uv_scale.mV[VY]);
- gGL.vertex2fv((x_min + height_vec - border_height_top).mV);
-
- gGL.texCoord2f(u_max, 1.f - border_uv_scale.mV[VY]);
- gGL.vertex2fv((x_max + height_vec - border_height_top).mV);
-
- gGL.texCoord2f(u_max, 1.f);
- gGL.vertex2fv((x_max + height_vec).mV);
-
- gGL.texCoord2f(u_min, 1.f);
- gGL.vertex2fv((x_min + height_vec).mV);
- }
-
- if (end_fragment > middle_start || start_fragment < middle_end)
- {
- x_min = border_width_left + ((llclamp(start_fragment, middle_start, middle_end) - middle_start)) * width_vec;
- x_max = border_width_left + ((llclamp(end_fragment, middle_start, middle_end) - middle_start)) * width_vec;
-
- // draw bottom middle
- gGL.texCoord2f(border_uv_scale.mV[VX], 0.f);
- gGL.vertex2fv(x_min.mV);
-
- gGL.texCoord2f(1.f - border_uv_scale.mV[VX], 0.f);
- gGL.vertex2fv((x_max).mV);
-
- gGL.texCoord2f(1.f - border_uv_scale.mV[VX], border_uv_scale.mV[VY]);
- gGL.vertex2fv((x_max + border_height_bottom).mV);
-
- gGL.texCoord2f(border_uv_scale.mV[VX], border_uv_scale.mV[VY]);
- gGL.vertex2fv((x_min + border_height_bottom).mV);
-
- // draw middle
- gGL.texCoord2f(border_uv_scale.mV[VX], border_uv_scale.mV[VY]);
- gGL.vertex2fv((x_min + border_height_bottom).mV);
-
- gGL.texCoord2f(1.f - border_uv_scale.mV[VX], border_uv_scale.mV[VY]);
- gGL.vertex2fv((x_max + border_height_bottom).mV);
-
- gGL.texCoord2f(1.f - border_uv_scale.mV[VX], 1.f - border_uv_scale.mV[VY]);
- gGL.vertex2fv((x_max + height_vec - border_height_top).mV);
-
- gGL.texCoord2f(border_uv_scale.mV[VX], 1.f - border_uv_scale.mV[VY]);
- gGL.vertex2fv((x_min + height_vec - border_height_top).mV);
-
- // draw top middle
- gGL.texCoord2f(border_uv_scale.mV[VX], 1.f - border_uv_scale.mV[VY]);
- gGL.vertex2fv((x_min + height_vec - border_height_top).mV);
-
- gGL.texCoord2f(1.f - border_uv_scale.mV[VX], 1.f - border_uv_scale.mV[VY]);
- gGL.vertex2fv((x_max + height_vec - border_height_top).mV);
-
- gGL.texCoord2f(1.f - border_uv_scale.mV[VX], 1.f);
- gGL.vertex2fv((x_max + height_vec).mV);
-
- gGL.texCoord2f(border_uv_scale.mV[VX], 1.f);
- gGL.vertex2fv((x_min + height_vec).mV);
- }
-
- if (end_fragment > middle_end)
- {
- u_min = (1.f - llmax(0.f, ((start_fragment - middle_end) / middle_start))) * border_uv_scale.mV[VX];
- u_max = (1.f - ((end_fragment - middle_end) / middle_start)) * border_uv_scale.mV[VX];
- x_min = width_vec - ((1.f - llmax(0.f, ((start_fragment - middle_end) / middle_start))) * border_width_right);
- x_max = width_vec - ((1.f - ((end_fragment - middle_end) / middle_start)) * border_width_right);
-
- // draw bottom right
- gGL.texCoord2f(u_min, 0.f);
- gGL.vertex2fv((x_min).mV);
-
- gGL.texCoord2f(u_max, 0.f);
- gGL.vertex2fv(x_max.mV);
-
- gGL.texCoord2f(u_max, border_uv_scale.mV[VY]);
- gGL.vertex2fv((x_max + border_height_bottom).mV);
-
- gGL.texCoord2f(u_min, border_uv_scale.mV[VY]);
- gGL.vertex2fv((x_min + border_height_bottom).mV);
-
- // draw right
- gGL.texCoord2f(u_min, border_uv_scale.mV[VY]);
- gGL.vertex2fv((x_min + border_height_bottom).mV);
-
- gGL.texCoord2f(u_max, border_uv_scale.mV[VY]);
- gGL.vertex2fv((x_max + border_height_bottom).mV);
-
- gGL.texCoord2f(u_max, 1.f - border_uv_scale.mV[VY]);
- gGL.vertex2fv((x_max + height_vec - border_height_top).mV);
-
- gGL.texCoord2f(u_min, 1.f - border_uv_scale.mV[VY]);
- gGL.vertex2fv((x_min + height_vec - border_height_top).mV);
-
- // draw top right
- gGL.texCoord2f(u_min, 1.f - border_uv_scale.mV[VY]);
- gGL.vertex2fv((x_min + height_vec - border_height_top).mV);
-
- gGL.texCoord2f(u_max, 1.f - border_uv_scale.mV[VY]);
- gGL.vertex2fv((x_max + height_vec - border_height_top).mV);
-
- gGL.texCoord2f(u_max, 1.f);
- gGL.vertex2fv((x_max + height_vec).mV);
-
- gGL.texCoord2f(u_min, 1.f);
- gGL.vertex2fv((x_min + height_vec).mV);
- }
- }
- gGL.end();
-
- gGL.popUIMatrix();
-}
-
-void gl_segmented_rect_3d_tex(const LLVector2& border_scale, const LLVector3& border_width,
- const LLVector3& border_height, const LLVector3& width_vec, const LLVector3& height_vec,
- const U32 edges)
-{
- LLVector3 left_border_width = ((edges & (~(U32)ROUNDED_RECT_RIGHT)) != 0) ? border_width : LLVector3::zero;
- LLVector3 right_border_width = ((edges & (~(U32)ROUNDED_RECT_LEFT)) != 0) ? border_width : LLVector3::zero;
-
- LLVector3 top_border_height = ((edges & (~(U32)ROUNDED_RECT_BOTTOM)) != 0) ? border_height : LLVector3::zero;
- LLVector3 bottom_border_height = ((edges & (~(U32)ROUNDED_RECT_TOP)) != 0) ? border_height : LLVector3::zero;
-
-
- gGL.begin(LLRender::QUADS);
- {
- // draw bottom left
- gGL.texCoord2f(0.f, 0.f);
- gGL.vertex3f(0.f, 0.f, 0.f);
-
- gGL.texCoord2f(border_scale.mV[VX], 0.f);
- gGL.vertex3fv(left_border_width.mV);
-
- gGL.texCoord2f(border_scale.mV[VX], border_scale.mV[VY]);
- gGL.vertex3fv((left_border_width + bottom_border_height).mV);
-
- gGL.texCoord2f(0.f, border_scale.mV[VY]);
- gGL.vertex3fv(bottom_border_height.mV);
-
- // draw bottom middle
- gGL.texCoord2f(border_scale.mV[VX], 0.f);
- gGL.vertex3fv(left_border_width.mV);
-
- gGL.texCoord2f(1.f - border_scale.mV[VX], 0.f);
- gGL.vertex3fv((width_vec - right_border_width).mV);
-
- gGL.texCoord2f(1.f - border_scale.mV[VX], border_scale.mV[VY]);
- gGL.vertex3fv((width_vec - right_border_width + bottom_border_height).mV);
-
- gGL.texCoord2f(border_scale.mV[VX], border_scale.mV[VY]);
- gGL.vertex3fv((left_border_width + bottom_border_height).mV);
-
- // draw bottom right
- gGL.texCoord2f(1.f - border_scale.mV[VX], 0.f);
- gGL.vertex3fv((width_vec - right_border_width).mV);
-
- gGL.texCoord2f(1.f, 0.f);
- gGL.vertex3fv(width_vec.mV);
-
- gGL.texCoord2f(1.f, border_scale.mV[VY]);
- gGL.vertex3fv((width_vec + bottom_border_height).mV);
-
- gGL.texCoord2f(1.f - border_scale.mV[VX], border_scale.mV[VY]);
- gGL.vertex3fv((width_vec - right_border_width + bottom_border_height).mV);
-
- // draw left
- gGL.texCoord2f(0.f, border_scale.mV[VY]);
- gGL.vertex3fv(bottom_border_height.mV);
-
- gGL.texCoord2f(border_scale.mV[VX], border_scale.mV[VY]);
- gGL.vertex3fv((left_border_width + bottom_border_height).mV);
-
- gGL.texCoord2f(border_scale.mV[VX], 1.f - border_scale.mV[VY]);
- gGL.vertex3fv((left_border_width + height_vec - top_border_height).mV);
-
- gGL.texCoord2f(0.f, 1.f - border_scale.mV[VY]);
- gGL.vertex3fv((height_vec - top_border_height).mV);
-
- // draw middle
- gGL.texCoord2f(border_scale.mV[VX], border_scale.mV[VY]);
- gGL.vertex3fv((left_border_width + bottom_border_height).mV);
-
- gGL.texCoord2f(1.f - border_scale.mV[VX], border_scale.mV[VY]);
- gGL.vertex3fv((width_vec - right_border_width + bottom_border_height).mV);
-
- gGL.texCoord2f(1.f - border_scale.mV[VX], 1.f - border_scale.mV[VY]);
- gGL.vertex3fv((width_vec - right_border_width + height_vec - top_border_height).mV);
-
- gGL.texCoord2f(border_scale.mV[VX], 1.f - border_scale.mV[VY]);
- gGL.vertex3fv((left_border_width + height_vec - top_border_height).mV);
-
- // draw right
- gGL.texCoord2f(1.f - border_scale.mV[VX], border_scale.mV[VY]);
- gGL.vertex3fv((width_vec - right_border_width + bottom_border_height).mV);
-
- gGL.texCoord2f(1.f, border_scale.mV[VY]);
- gGL.vertex3fv((width_vec + bottom_border_height).mV);
-
- gGL.texCoord2f(1.f, 1.f - border_scale.mV[VY]);
- gGL.vertex3fv((width_vec + height_vec - top_border_height).mV);
-
- gGL.texCoord2f(1.f - border_scale.mV[VX], 1.f - border_scale.mV[VY]);
- gGL.vertex3fv((width_vec - right_border_width + height_vec - top_border_height).mV);
-
- // draw top left
- gGL.texCoord2f(0.f, 1.f - border_scale.mV[VY]);
- gGL.vertex3fv((height_vec - top_border_height).mV);
-
- gGL.texCoord2f(border_scale.mV[VX], 1.f - border_scale.mV[VY]);
- gGL.vertex3fv((left_border_width + height_vec - top_border_height).mV);
-
- gGL.texCoord2f(border_scale.mV[VX], 1.f);
- gGL.vertex3fv((left_border_width + height_vec).mV);
-
- gGL.texCoord2f(0.f, 1.f);
- gGL.vertex3fv((height_vec).mV);
-
- // draw top middle
- gGL.texCoord2f(border_scale.mV[VX], 1.f - border_scale.mV[VY]);
- gGL.vertex3fv((left_border_width + height_vec - top_border_height).mV);
-
- gGL.texCoord2f(1.f - border_scale.mV[VX], 1.f - border_scale.mV[VY]);
- gGL.vertex3fv((width_vec - right_border_width + height_vec - top_border_height).mV);
-
- gGL.texCoord2f(1.f - border_scale.mV[VX], 1.f);
- gGL.vertex3fv((width_vec - right_border_width + height_vec).mV);
-
- gGL.texCoord2f(border_scale.mV[VX], 1.f);
- gGL.vertex3fv((left_border_width + height_vec).mV);
-
- // draw top right
- gGL.texCoord2f(1.f - border_scale.mV[VX], 1.f - border_scale.mV[VY]);
- gGL.vertex3fv((width_vec - right_border_width + height_vec - top_border_height).mV);
-
- gGL.texCoord2f(1.f, 1.f - border_scale.mV[VY]);
- gGL.vertex3fv((width_vec + height_vec - top_border_height).mV);
-
- gGL.texCoord2f(1.f, 1.f);
- gGL.vertex3fv((width_vec + height_vec).mV);
-
- gGL.texCoord2f(1.f - border_scale.mV[VX], 1.f);
- gGL.vertex3fv((width_vec - right_border_width + height_vec).mV);
- }
- gGL.end();
-
-}
-
-void gl_segmented_rect_3d_tex_top(const LLVector2& border_scale, const LLVector3& border_width, const LLVector3& border_height, const LLVector3& width_vec, const LLVector3& height_vec)
-{
- gl_segmented_rect_3d_tex(border_scale, border_width, border_height, width_vec, height_vec, ROUNDED_RECT_TOP);
}
void LLUI::initClass(const settings_map_t& settings,
LLImageProviderInterface* image_provider,
LLUIAudioCallback audio_callback,
+ LLUIAudioCallback deferred_audio_callback,
const LLVector2* scale_factor,
const std::string& language)
{
+ LLRender2D::initClass(image_provider,scale_factor);
sSettingGroups = settings;
if ((get_ptr_in_map(sSettingGroups, std::string("config")) == NULL) ||
(get_ptr_in_map(sSettingGroups, std::string("floater")) == NULL) ||
(get_ptr_in_map(sSettingGroups, std::string("ignores")) == NULL))
{
- llerrs << "Failure to initialize configuration groups" << llendl;
+ LL_ERRS() << "Failure to initialize configuration groups" << LL_ENDL;
}
- sImageProvider = image_provider;
sAudioCallback = audio_callback;
- sGLScaleFactor = (scale_factor == NULL) ? LLVector2(1.f, 1.f) : *scale_factor;
+ sDeferredAudioCallback = deferred_audio_callback;
sWindow = NULL; // set later in startup
LLFontGL::sShadowColor = LLUIColorTable::instance().getColor("ColorDropShadow");
LLUICtrl::CommitCallbackRegistry::Registrar& reg = LLUICtrl::CommitCallbackRegistry::defaultRegistrar();
- // Callbacks for associating controls with floater visibilty:
- reg.add("Floater.Toggle", boost::bind(&LLFloaterReg::toggleFloaterInstance, _2));
- reg.add("Floater.Show", boost::bind(&LLFloaterReg::showFloaterInstance, _2));
- reg.add("Floater.Hide", boost::bind(&LLFloaterReg::hideFloaterInstance, _2));
- reg.add("Floater.InitToVisibilityControl", boost::bind(&LLFloaterReg::initUICtrlToFloaterVisibilityControl, _1, _2));
+ // Callbacks for associating controls with floater visibility:
+ reg.add("Floater.Toggle", boost::bind(&LLFloaterReg::toggleInstance, _2, LLSD()));
+ reg.add("Floater.ToggleOrBringToFront", boost::bind(&LLFloaterReg::toggleInstanceOrBringToFront, _2, LLSD()));
+ reg.add("Floater.Show", boost::bind(&LLFloaterReg::showInstance, _2, LLSD(), FALSE));
+ reg.add("Floater.Hide", boost::bind(&LLFloaterReg::hideInstance, _2, LLSD()));
// Button initialization callback for toggle buttons
reg.add("Button.SetFloaterToggle", boost::bind(&LLButton::setFloaterToggle, _1, _2));
- // Button initialization callback for toggle buttons on dockale floaters
+ // Button initialization callback for toggle buttons on dockable floaters
reg.add("Button.SetDockableFloaterToggle", boost::bind(&LLButton::setDockableFloaterToggle, _1, _2));
// Display the help topic for the current context
@@ -1616,16 +198,17 @@ void LLUI::initClass(const settings_map_t& settings,
// Currently unused, but kept for reference:
reg.add("Button.ToggleFloater", boost::bind(&LLButton::toggleFloaterAndSetToggleState, _1, _2));
- // Used by menus along with Floater.Toggle to display visibility as a checkmark
- LLUICtrl::EnableCallbackRegistry::defaultRegistrar().add("Floater.Visible", boost::bind(&LLFloaterReg::floaterInstanceVisible, _2));
+ // Used by menus along with Floater.Toggle to display visibility as a check-mark
+ LLUICtrl::EnableCallbackRegistry::defaultRegistrar().add("Floater.Visible", boost::bind(&LLFloaterReg::instanceVisible, _2, LLSD()));
+ LLUICtrl::EnableCallbackRegistry::defaultRegistrar().add("Floater.IsOpen", boost::bind(&LLFloaterReg::instanceVisible, _2, LLSD()));
+
+ // Parse the master list of commands
+ LLCommandManager::load();
}
void LLUI::cleanupClass()
{
- if(sImageProvider)
- {
- sImageProvider->cleanUp();
- }
+ LLRender2D::cleanupClass();
}
void LLUI::setPopupFuncs(const add_popup_t& add_popup, const remove_popup_t& remove_popup, const clear_popups_t& clear_popups)
@@ -1649,64 +232,14 @@ void LLUI::dirtyRect(LLRect rect)
}
}
-
-//static
-void LLUI::translate(F32 x, F32 y, F32 z)
-{
- gGL.translateUI(x,y,z);
- LLFontGL::sCurOrigin.mX += (S32) x;
- LLFontGL::sCurOrigin.mY += (S32) y;
- LLFontGL::sCurOrigin.mZ += z;
-}
-
-//static
-void LLUI::pushMatrix()
-{
- gGL.pushUIMatrix();
- LLFontGL::sOriginStack.push_back(LLFontGL::sCurOrigin);
-}
-
-//static
-void LLUI::popMatrix()
-{
- gGL.popUIMatrix();
- LLFontGL::sCurOrigin = *LLFontGL::sOriginStack.rbegin();
- LLFontGL::sOriginStack.pop_back();
-}
-
-//static
-void LLUI::loadIdentity()
-{
- gGL.loadUIIdentity();
- LLFontGL::sCurOrigin.mX = 0;
- LLFontGL::sCurOrigin.mY = 0;
- LLFontGL::sCurOrigin.mZ = 0;
-}
-
-//static
-void LLUI::setScaleFactor(const LLVector2 &scale_factor)
-{
- sGLScaleFactor = scale_factor;
-}
-
-//static
-void LLUI::setLineWidth(F32 width)
-{
- gGL.flush();
- glLineWidth(width * lerp(sGLScaleFactor.mV[VX], sGLScaleFactor.mV[VY], 0.5f));
-}
-
//static
void LLUI::setMousePositionScreen(S32 x, S32 y)
{
S32 screen_x, screen_y;
- screen_x = llround((F32)x * sGLScaleFactor.mV[VX]);
- screen_y = llround((F32)y * sGLScaleFactor.mV[VY]);
+ screen_x = ll_round((F32)x * getScaleFactor().mV[VX]);
+ screen_y = ll_round((F32)y * getScaleFactor().mV[VY]);
- LLCoordWindow window_point;
- LLView::getWindow()->convertCoords(LLCoordGL(screen_x, screen_y), &window_point);
-
- LLView::getWindow()->setCursorPosition(window_point);
+ LLView::getWindow()->setCursorPosition(LLCoordGL(screen_x, screen_y).convert());
}
//static
@@ -1714,10 +247,9 @@ 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]);
+ LLCoordGL cursor_pos_gl(cursor_pos_window.convert());
+ *x = ll_round((F32)cursor_pos_gl.mX / getScaleFactor().mV[VX]);
+ *y = ll_round((F32)cursor_pos_gl.mY / getScaleFactor().mV[VX]);
}
//static
@@ -1793,85 +325,37 @@ struct Paths : public LLInitParam::Block<Paths>
{}
};
-//static
-void LLUI::setupPaths()
-{
- std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_SKINS, "paths.xml");
-
- LLXMLNodePtr root;
- BOOL success = LLXMLNode::parseFile(filename, root, NULL);
- Paths paths;
- LLXUIParser parser;
- parser.readXUI(root, paths, filename);
-
- sXUIPaths.clear();
-
- if (success && paths.validateBlock())
- {
- LLStringUtil::format_map_t path_args;
- path_args["[LANGUAGE]"] = LLUI::getLanguage();
-
- for (LLInitParam::ParamIterator<Directory>::const_iterator it = paths.directories.begin(),
- end_it = paths.directories.end();
- it != end_it;
- ++it)
- {
- 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
- {
- std::string slash = gDirUtilp->getDirDelimiter();
- std::string dir = "xui" + slash + "en";
- llwarns << "XUI::config file unable to open: " << filename << llendl;
- sXUIPaths.push_back(dir);
- }
-}
-
//static
std::string LLUI::locateSkin(const std::string& filename)
{
- std::string slash = gDirUtilp->getDirDelimiter();
std::string found_file = filename;
- if (!gDirUtilp->fileExists(found_file))
+ if (gDirUtilp->fileExists(found_file))
{
- found_file = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, filename); // Should be CUSTOM_SKINS?
+ return found_file;
}
- if (sSettingGroups["config"] && sSettingGroups["config"]->controlExists("Language"))
+
+ found_file = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, filename); // Should be CUSTOM_SKINS?
+ if (gDirUtilp->fileExists(found_file))
{
- if (!gDirUtilp->fileExists(found_file))
- {
- std::string localization = getLanguage();
- std::string local_skin = "xui" + slash + localization + slash + filename;
- found_file = gDirUtilp->findSkinnedFilename(local_skin);
- }
+ return found_file;
}
- if (!gDirUtilp->fileExists(found_file))
+
+ found_file = gDirUtilp->findSkinnedFilename(LLDir::XUI, filename);
+ if (! found_file.empty())
{
- std::string local_skin = "xui" + slash + "en" + slash + filename;
- found_file = gDirUtilp->findSkinnedFilename(local_skin);
+ return found_file;
}
- if (!gDirUtilp->fileExists(found_file))
+
+ found_file = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, filename);
+ if (gDirUtilp->fileExists(found_file))
{
- found_file = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, filename);
+ return found_file;
}
- return found_file;
-}
+ LL_WARNS("LLUI") << "Can't find '" << filename
+ << "' in user settings, any skin directory or app_settings" << LL_ENDL;
+ return "";
+}
//static
LLVector2 LLUI::getWindowSize()
@@ -1879,21 +363,21 @@ LLVector2 LLUI::getWindowSize()
LLCoordWindow window_rect;
sWindow->getSize(&window_rect);
- return LLVector2(window_rect.mX / sGLScaleFactor.mV[VX], window_rect.mY / sGLScaleFactor.mV[VY]);
+ return LLVector2(window_rect.mX / getScaleFactor().mV[VX], window_rect.mY / getScaleFactor().mV[VY]);
}
//static
void LLUI::screenPointToGL(S32 screen_x, S32 screen_y, S32 *gl_x, S32 *gl_y)
{
- *gl_x = llround((F32)screen_x * sGLScaleFactor.mV[VX]);
- *gl_y = llround((F32)screen_y * sGLScaleFactor.mV[VY]);
+ *gl_x = ll_round((F32)screen_x * getScaleFactor().mV[VX]);
+ *gl_y = ll_round((F32)screen_y * getScaleFactor().mV[VY]);
}
//static
void LLUI::glPointToScreen(S32 gl_x, S32 gl_y, S32 *screen_x, S32 *screen_y)
{
- *screen_x = llround((F32)gl_x / sGLScaleFactor.mV[VX]);
- *screen_y = llround((F32)gl_y / sGLScaleFactor.mV[VY]);
+ *screen_x = ll_round((F32)gl_x / getScaleFactor().mV[VX]);
+ *screen_y = ll_round((F32)gl_y / getScaleFactor().mV[VY]);
}
//static
@@ -1910,27 +394,6 @@ void LLUI::glRectToScreen(const LLRect& gl, LLRect *screen)
glPointToScreen(gl.mRight, gl.mBottom, &screen->mRight, &screen->mBottom);
}
-//static
-LLPointer<LLUIImage> LLUI::getUIImageByID(const LLUUID& image_id, S32 priority)
-{
- if (sImageProvider)
- {
- return sImageProvider->getUIImageByID(image_id, priority);
- }
- else
- {
- return NULL;
- }
-}
-
-//static
-LLPointer<LLUIImage> LLUI::getUIImage(const std::string& name, S32 priority)
-{
- if (!name.empty() && sImageProvider)
- return sImageProvider->getUIImage(name, priority);
- else
- return NULL;
-}
LLControlGroup& LLUI::getControlControlGroup (const std::string& controlname)
{
@@ -2011,13 +474,60 @@ void LLUI::positionViewNearMouse(LLView* view, S32 spawn_x, S32 spawn_y)
CURSOR_HEIGHT + MOUSE_CURSOR_PADDING * 2);
S32 local_x, local_y;
- // convert screen coordinates to tooltipview-local coordinates
+ // convert screen coordinates to tooltip view-local coordinates
parent->screenPointToLocal(spawn_x, spawn_y, &local_x, &local_y);
// Start at spawn position (using left/top)
view->setOrigin( local_x, local_y - view->getRect().getHeight());
- // Make sure we're onscreen and not overlapping the mouse
- view->translateIntoRectWithExclusion( virtual_window_rect, mouse_rect, FALSE );
+ // Make sure we're on-screen and not overlapping the mouse
+ view->translateIntoRectWithExclusion( virtual_window_rect, mouse_rect );
+}
+
+LLView* LLUI::resolvePath(LLView* context, const std::string& path)
+{
+ // Nothing about resolvePath() should require non-const LLView*. If caller
+ // wants non-const, call the const flavor and then cast away const-ness.
+ return const_cast<LLView*>(resolvePath(const_cast<const LLView*>(context), path));
+}
+
+const LLView* LLUI::resolvePath(const LLView* context, const std::string& path)
+{
+ // Create an iterator over slash-separated parts of 'path'. Dereferencing
+ // this iterator returns an iterator_range over the substring. Unlike
+ // LLStringUtil::getTokens(), this split_iterator doesn't combine adjacent
+ // delimiters: leading/trailing slash produces an empty substring, double
+ // slash produces an empty substring. That's what we need.
+ boost::split_iterator<std::string::const_iterator> ti(path, boost::first_finder("/")), tend;
+
+ if (ti == tend)
+ {
+ // 'path' is completely empty, no navigation
+ return context;
+ }
+
+ // leading / means "start at root"
+ if (ti->empty())
+ {
+ context = getRootView();
+ ++ti;
+ }
+
+ bool recurse = false;
+ for (; ti != tend && context; ++ti)
+ {
+ if (ti->empty())
+ {
+ recurse = true;
+ }
+ else
+ {
+ std::string part(ti->begin(), ti->end());
+ context = context->findChildView(part, recurse);
+ recurse = false;
+ }
+ }
+
+ return context;
}
@@ -2025,46 +535,37 @@ void LLUI::positionViewNearMouse(LLView* view, S32 spawn_x, S32 spawn_y)
namespace LLInitParam
{
- 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),
+ ParamValue<LLUIColor>::ParamValue(const LLUIColor& color)
+ : super_t(color),
red("red"),
green("green"),
blue("blue"),
alpha("alpha"),
control("")
{
- setBlockFromValue();
+ updateBlockFromValue(false);
}
- void TypedParam<LLUIColor>::setValueFromBlock() const
+ void ParamValue<LLUIColor>::updateValueFromBlock()
{
- if (control.isProvided())
+ if (control.isProvided() && !control().empty())
{
- mData.mValue = LLUIColorTable::instance().getColor(control);
+ updateValue(LLUIColorTable::instance().getColor(control));
}
else
{
- mData.mValue = LLColor4(red, green, blue, alpha);
+ updateValue(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()
+ void ParamValue<LLUIColor>::updateBlockFromValue(bool make_block_authoritative)
{
- declare("white", LLColor4::white);
- declare("black", LLColor4::black);
- declare("red", LLColor4::red);
- declare("green", LLColor4::green);
- declare("blue", LLColor4::blue);
+ LLColor4 color = getValue();
+ red.set(color.mV[VRED], make_block_authoritative);
+ green.set(color.mV[VGREEN], make_block_authoritative);
+ blue.set(color.mV[VBLUE], make_block_authoritative);
+ alpha.set(color.mV[VALPHA], make_block_authoritative);
+ control.set("", make_block_authoritative);
}
bool ParamCompare<const LLFontGL*, false>::equals(const LLFontGL* a, const LLFontGL* b)
@@ -2073,23 +574,26 @@ namespace LLInitParam
&& !(b->getFontDesc() < a->getFontDesc());
}
- 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),
+ ParamValue<const LLFontGL*>::ParamValue(const LLFontGL* fontp)
+ : super_t(fontp),
name("name"),
size("size"),
style("style")
{
- setBlockFromValue();
+ if (!fontp)
+ {
+ updateValue(LLFontGL::getFontDefault());
+ }
addSynonym(name, "");
- setBlockFromValue();
+ updateBlockFromValue(false);
}
- void TypedParam<const LLFontGL*>::setValueFromBlock() const
+ void ParamValue<const LLFontGL*>::updateValueFromBlock()
{
const LLFontGL* res_fontp = LLFontGL::getFontByName(name);
if (res_fontp)
{
- mData.mValue = res_fontp;
+ updateValue(res_fontp);
return;
}
@@ -2099,22 +603,26 @@ namespace LLInitParam
const LLFontGL* fontp = LLFontGL::getFont(desc);
if (fontp)
{
- mData.mValue = fontp;
- }
+ updateValue(fontp);
+ }
+ else
+ {
+ updateValue(LLFontGL::getFontDefault());
+ }
}
- void TypedParam<const LLFontGL*>::setBlockFromValue()
+ void ParamValue<const LLFontGL*>::updateBlockFromValue(bool make_block_authoritative)
{
- if (mData.mValue)
+ if (getValue())
{
- name.set(LLFontGL::nameFromFont(mData.mValue), false);
- size.set(LLFontGL::sizeFromFont(mData.mValue), false);
- style.set(LLFontGL::getStringFromStyle(mData.mValue->getFontDesc().getStyle()), false);
+ name.set(LLFontGL::nameFromFont(getValue()), make_block_authoritative);
+ size.set(LLFontGL::sizeFromFont(getValue()), make_block_authoritative);
+ style.set(LLFontGL::getStringFromStyle(getValue()->getFontDesc().getStyle()), make_block_authoritative);
}
}
- 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),
+ ParamValue<LLRect>::ParamValue(const LLRect& rect)
+ : super_t(rect),
left("left"),
top("top"),
right("right"),
@@ -2122,10 +630,10 @@ namespace LLInitParam
width("width"),
height("height")
{
- setBlockFromValue();
+ updateBlockFromValue(false);
}
- void TypedParam<LLRect>::setValueFromBlock() const
+ void ParamValue<LLRect>::updateValueFromBlock()
{
LLRect rect;
@@ -2186,40 +694,43 @@ namespace LLInitParam
rect.mBottom = bottom;
rect.mTop = top;
}
- mData.mValue = rect;
+ updateValue(rect);
}
- void TypedParam<LLRect>::setBlockFromValue()
+ void ParamValue<LLRect>::updateBlockFromValue(bool make_block_authoritative)
{
// 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);
+ // we use the lowest priority pairing so that any valid pairing in xui
+ // will override those calculated from the rect object
+ // in this case, that is left+width and bottom+height
+ LLRect& value = getValue();
+
+ right.set(value.mRight, false);
+ left.set(value.mLeft, make_block_authoritative);
+ width.set(value.getWidth(), make_block_authoritative);
+
+ top.set(value.mTop, false);
+ bottom.set(value.mBottom, make_block_authoritative);
+ height.set(value.getHeight(), make_block_authoritative);
}
- 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),
+ ParamValue<LLCoordGL>::ParamValue(const LLCoordGL& coord)
+ : super_t(coord),
x("x"),
y("y")
{
- setBlockFromValue();
+ updateBlockFromValue(false);
}
- void TypedParam<LLCoordGL>::setValueFromBlock() const
+ void ParamValue<LLCoordGL>::updateValueFromBlock()
{
- mData.mValue.set(x, y);
+ updateValue(LLCoordGL(x, y));
}
- void TypedParam<LLCoordGL>::setBlockFromValue()
+ void ParamValue<LLCoordGL>::updateBlockFromValue(bool make_block_authoritative)
{
- x.set(mData.mValue.mX, false);
- y.set(mData.mValue.mY, false);
+ x.set(getValue().mX, make_block_authoritative);
+ y.set(getValue().mY, make_block_authoritative);
}
diff --git a/indra/llui/llui.h b/indra/llui/llui.h
index 62d10df8b2..c727f75c4f 100644..100755
--- a/indra/llui/llui.h
+++ b/indra/llui/llui.h
@@ -1,6 +1,6 @@
/**
* @file llui.h
- * @brief GL function declarations and other general static UI services.
+ * @brief General static UI services.
*
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
@@ -24,121 +24,86 @@
* $/LicenseInfo$
*/
-// All immediate-mode gl drawing should happen here.
#ifndef LL_LLUI_H
#define LL_LLUI_H
-#include "llpointer.h" // LLPointer<>
#include "llrect.h"
+#include "llcoord.h"
#include "llcontrol.h"
#include "llcoord.h"
-#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 "llcontrol.h"
+#include "llglslshader.h"
#include "llinitparam.h"
#include "llregistry.h"
+#include "llrender2dutils.h"
+#include "llpointer.h"
#include "lluicolor.h"
#include "lluicolortable.h"
+#include "lluiimage.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"
+#include "v2math.h"
+#include <limits>
// for initparam specialization
#include "llfontgl.h"
-class LLColor4;
-class LLVector3;
-class LLVector2;
-class LLUIImage;
+
class LLUUID;
class LLWindow;
class LLView;
class LLHelp;
-// UI colors
-extern const LLColor4 UI_VERTEX_COLOR;
-void make_ui_sound(const char* name);
-BOOL ui_point_in_rect(S32 x, S32 y, S32 left, S32 top, S32 right, S32 bottom);
-void gl_state_for_2d(S32 width, S32 height);
-
-void gl_line_2d(S32 x1, S32 y1, S32 x2, S32 y2);
-void gl_line_2d(S32 x1, S32 y1, S32 x2, S32 y2, const LLColor4 &color );
-void gl_triangle_2d(S32 x1, S32 y1, S32 x2, S32 y2, S32 x3, S32 y3, const LLColor4& color, BOOL filled);
-void gl_rect_2d_simple( S32 width, S32 height );
-
-void gl_draw_x(const LLRect& rect, const LLColor4& color);
-
-void gl_rect_2d(S32 left, S32 top, S32 right, S32 bottom, BOOL filled = TRUE );
-void gl_rect_2d(S32 left, S32 top, S32 right, S32 bottom, const LLColor4 &color, BOOL filled = TRUE );
-void gl_rect_2d_offset_local( S32 left, S32 top, S32 right, S32 bottom, const LLColor4 &color, S32 pixel_offset = 0, BOOL filled = TRUE );
-void gl_rect_2d_offset_local( S32 left, S32 top, S32 right, S32 bottom, S32 pixel_offset = 0, BOOL filled = TRUE );
-void gl_rect_2d(const LLRect& rect, BOOL filled = TRUE );
-void gl_rect_2d(const LLRect& rect, const LLColor4& color, BOOL filled = TRUE );
-void gl_rect_2d_checkerboard(const LLRect& rect, GLfloat alpha = 1.0f);
-
-void gl_drop_shadow(S32 left, S32 top, S32 right, S32 bottom, const LLColor4 &start_color, S32 lines);
-
-void gl_circle_2d(F32 x, F32 y, F32 radius, S32 steps, BOOL filled);
-void gl_arc_2d(F32 center_x, F32 center_y, F32 radius, S32 steps, BOOL filled, F32 start_angle, F32 end_angle);
-void gl_deep_circle( F32 radius, F32 depth );
-void gl_ring( F32 radius, F32 width, const LLColor4& center_color, const LLColor4& side_color, S32 steps, BOOL render_center );
-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_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 );
-
-// segmented rectangles
-
-/*
- TL |______TOP_________| TR
- /| |\
- _/_|__________________|_\_
- L| | MIDDLE | |R
- _|_|__________________|_|_
- \ | BOTTOM | /
- BL\|__________________|/ BR
- | |
-*/
-
-typedef enum e_rounded_edge
+// this enum is used by the llview.h (viewer) and the llassetstorage.h (viewer and sim)
+enum EDragAndDropType
{
- ROUNDED_RECT_LEFT = 0x1,
- ROUNDED_RECT_TOP = 0x2,
- ROUNDED_RECT_RIGHT = 0x4,
- ROUNDED_RECT_BOTTOM = 0x8,
- ROUNDED_RECT_ALL = 0xf
-}ERoundedEdge;
-
-
-void gl_segmented_rect_2d_tex(const S32 left, const S32 top, const S32 right, const S32 bottom, const S32 texture_width, const S32 texture_height, const S32 border_size, const U32 edges = ROUNDED_RECT_ALL);
-void gl_segmented_rect_2d_fragment_tex(const S32 left, const S32 top, const S32 right, const S32 bottom, const S32 texture_width, const S32 texture_height, const S32 border_size, const F32 start_fragment, const F32 end_fragment, const U32 edges = ROUNDED_RECT_ALL);
-void gl_segmented_rect_3d_tex(const LLVector2& border_scale, const LLVector3& border_width, const LLVector3& border_height, const LLVector3& width_vec, const LLVector3& height_vec, U32 edges = ROUNDED_RECT_ALL);
-void gl_segmented_rect_3d_tex_top(const LLVector2& border_scale, const LLVector3& border_width, const LLVector3& border_height, const LLVector3& width_vec, const LLVector3& height_vec);
+ DAD_NONE = 0,
+ DAD_TEXTURE = 1,
+ DAD_SOUND = 2,
+ DAD_CALLINGCARD = 3,
+ DAD_LANDMARK = 4,
+ DAD_SCRIPT = 5,
+ DAD_CLOTHING = 6,
+ DAD_OBJECT = 7,
+ DAD_NOTECARD = 8,
+ DAD_CATEGORY = 9,
+ DAD_ROOT_CATEGORY = 10,
+ DAD_BODYPART = 11,
+ DAD_ANIMATION = 12,
+ DAD_GESTURE = 13,
+ DAD_LINK = 14,
+ DAD_MESH = 15,
+ DAD_WIDGET = 16,
+ DAD_PERSON = 17,
+ DAD_COUNT = 18, // number of types in this enum
+};
-inline void gl_rect_2d( const LLRect& rect, BOOL filled )
+// Reasons for drags to be denied.
+// ordered by priority for multi-drag
+enum EAcceptance
{
- gl_rect_2d( rect.mLeft, rect.mTop, rect.mRight, rect.mBottom, filled );
-}
+ ACCEPT_POSTPONED, // we are asynchronously determining acceptance
+ ACCEPT_NO, // Uninformative, general purpose denial.
+ ACCEPT_NO_CUSTOM, // Denial with custom message.
+ ACCEPT_NO_LOCKED, // Operation would be valid, but permissions are set to disallow it.
+ ACCEPT_YES_COPY_SINGLE, // We'll take a copy of a single item
+ ACCEPT_YES_SINGLE, // Accepted. OK to drag and drop single item here.
+ ACCEPT_YES_COPY_MULTI, // We'll take a copy of multiple items
+ ACCEPT_YES_MULTI // Accepted. OK to drag and drop multiple items here.
+};
-inline void gl_rect_2d_offset_local( const LLRect& rect, S32 pixel_offset, BOOL filled)
+enum EAddPosition
{
- gl_rect_2d_offset_local( rect.mLeft, rect.mTop, rect.mRight, rect.mBottom, pixel_offset, filled );
-}
+ ADD_TOP,
+ ADD_BOTTOM,
+ ADD_DEFAULT
+};
+
+
+void make_ui_sound(const char* name);
+void make_ui_sound_deferred(const char * name);
class LLImageProviderInterface;
@@ -149,6 +114,122 @@ class LLUI
LOG_CLASS(LLUI);
public:
//
+ // Classes
+ //
+
+ struct RangeS32
+ {
+ struct Params : public LLInitParam::Block<Params>
+ {
+ Optional<S32> minimum,
+ maximum;
+
+ Params()
+ : minimum("min", 0),
+ maximum("max", S32_MAX)
+ {}
+ };
+
+ // correct for inverted params
+ RangeS32(const Params& p = Params())
+ : mMin(p.minimum),
+ mMax(p.maximum)
+ {
+ sanitizeRange();
+ }
+
+ RangeS32(S32 minimum, S32 maximum)
+ : mMin(minimum),
+ mMax(maximum)
+ {
+ sanitizeRange();
+ }
+
+ S32 clamp(S32 input)
+ {
+ if (input < mMin) return mMin;
+ if (input > mMax) return mMax;
+ return input;
+ }
+
+ void setRange(S32 minimum, S32 maximum)
+ {
+ mMin = minimum;
+ mMax = maximum;
+ sanitizeRange();
+ }
+
+ S32 getMin() { return mMin; }
+ S32 getMax() { return mMax; }
+
+ bool operator==(const RangeS32& other) const
+ {
+ return mMin == other.mMin
+ && mMax == other.mMax;
+ }
+ private:
+ void sanitizeRange()
+ {
+ if (mMin > mMax)
+ {
+ LL_WARNS() << "Bad interval range (" << mMin << ", " << mMax << ")" << LL_ENDL;
+ // since max is usually the most dangerous one to ignore (buffer overflow, etc), prefer it
+ // in the case of a malformed range
+ mMin = mMax;
+ }
+ }
+
+
+ S32 mMin,
+ mMax;
+ };
+
+ struct ClampedS32 : public RangeS32
+ {
+ struct Params : public LLInitParam::Block<Params, RangeS32::Params>
+ {
+ Mandatory<S32> value;
+
+ Params()
+ : value("", 0)
+ {
+ addSynonym(value, "value");
+ }
+ };
+
+ ClampedS32(const Params& p)
+ : RangeS32(p)
+ {}
+
+ ClampedS32(const RangeS32& range)
+ : RangeS32(range)
+ {
+ // set value here, after range has been sanitized
+ mValue = clamp(0);
+ }
+
+ ClampedS32(S32 value, const RangeS32& range = RangeS32())
+ : RangeS32(range)
+ {
+ mValue = clamp(value);
+ }
+
+ S32 get()
+ {
+ return mValue;
+ }
+
+ void set(S32 value)
+ {
+ mValue = clamp(value);
+ }
+
+
+ private:
+ S32 mValue;
+ };
+
+ //
// Methods
//
typedef std::map<std::string, LLControlGroup*> settings_map_t;
@@ -159,15 +240,16 @@ public:
static void initClass(const settings_map_t& settings,
LLImageProviderInterface* image_provider,
LLUIAudioCallback audio_callback = NULL,
+ LLUIAudioCallback deferred_audio_callback = NULL,
const LLVector2 *scale_factor = NULL,
const std::string& language = LLStringUtil::null);
static void cleanupClass();
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 void pushMatrix() { LLRender2D::pushMatrix(); }
+ static void popMatrix() { LLRender2D::popMatrix(); }
+ static void loadIdentity() { LLRender2D::loadIdentity(); }
+ static void translate(F32 x, F32 y, F32 z = 0.0f) { LLRender2D::translate(x, y, z); }
static LLRect sDirtyRect;
static BOOL sDirty;
@@ -176,24 +258,49 @@ public:
// 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();
-
- static void setupPaths();
- static const std::vector<std::string>& getXUIPaths() { return sXUIPaths; }
- static std::string getSkinPath() { return sXUIPaths.front(); }
- static std::string getLocalizedSkinPath() { return sXUIPaths.back(); } //all files may not exist at the localized path
//helper functions (should probably move free standing rendering helper functions here)
static LLView* getRootView() { return sRootView; }
static void setRootView(LLView* view) { sRootView = view; }
+ /**
+ * Walk the LLView tree to resolve a path
+ * Paths can be discovered using Develop > XUI > Show XUI Paths
+ *
+ * A leading "/" indicates the root of the tree is the starting
+ * position of the search, (otherwise the context node is used)
+ *
+ * Adjacent "//" mean that the next level of the search is done
+ * recursively ("descendant" rather than "child").
+ *
+ * Return values: If no match is found, NULL is returned,
+ * otherwise the matching LLView* is returned.
+ *
+ * Examples:
+ *
+ * "/" -> return the root view
+ * "/foo" -> find "foo" as a direct child of the root
+ * "foo" -> find "foo" as a direct child of the context node
+ * "//foo" -> find the first "foo" child anywhere in the tree
+ * "/foo/bar" -> find "foo" as direct child of the root, and
+ * "bar" as a direct child of "foo"
+ * "//foo//bar/baz" -> find the first "foo" anywhere in the
+ * tree, the first "bar" anywhere under it, and "baz"
+ * as a direct child of that
+ */
+ static const LLView* resolvePath(const LLView* context, const std::string& path);
+ static LLView* resolvePath(LLView* context, const std::string& path);
static std::string locateSkin(const std::string& filename);
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, S32 priority = 0);
- static LLPointer<LLUIImage> getUIImage(const std::string& name, S32 priority = 0);
+ static LLVector2& getScaleFactor() { return LLRender2D::sGLScaleFactor; }
+ static void setScaleFactor(const LLVector2& scale_factor) { LLRender2D::setScaleFactor(scale_factor); }
+ static void setLineWidth(F32 width) { LLRender2D::setLineWidth(width); }
+ static LLPointer<LLUIImage> getUIImageByID(const LLUUID& image_id, S32 priority = 0)
+ { return LLRender2D::getUIImageByID(image_id, priority); }
+ static LLPointer<LLUIImage> getUIImage(const std::string& name, S32 priority = 0)
+ { return LLRender2D::getUIImage(name, priority); }
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);
@@ -222,12 +329,11 @@ public:
//
static settings_map_t sSettingGroups;
static LLUIAudioCallback sAudioCallback;
- static LLVector2 sGLScaleFactor;
+ static LLUIAudioCallback sDeferredAudioCallback;
static LLWindow* sWindow;
static LLView* sRootView;
static LLHelp* sHelpImpl;
private:
- static LLImageProviderInterface* sImageProvider;
static std::vector<std::string> sXUIPaths;
static LLFrameTimer sMouseIdleTimer;
static add_popup_t sAddPopupFunc;
@@ -238,20 +344,6 @@ private:
// Moved LLLocalClipRect to lllocalcliprect.h
-// Moved all LLHandle-related code to llhandle.h
-
-//RN: maybe this needs to moved elsewhere?
-class LLImageProviderInterface
-{
-protected:
- LLImageProviderInterface() {};
- virtual ~LLImageProviderInterface() {};
-public:
- 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;
-};
-
class LLCallbackRegistry
{
public:
@@ -300,10 +392,11 @@ public:
// this avoids a MSVC bug where non-referenced static members are "optimized" away
// even if their constructors have side effects
- void reference()
+ S32 reference()
{
S32 dummy;
dummy = 0;
+ return dummy;
}
};
@@ -318,7 +411,7 @@ private:
static void initClass()
{
- llerrs << "No static initClass() method defined for " << typeid(T).name() << llendl;
+ LL_ERRS() << "No static initClass() method defined for " << typeid(T).name() << LL_ENDL;
}
};
@@ -333,7 +426,7 @@ private:
static void destroyClass()
{
- llerrs << "No static destroyClass() method defined for " << typeid(T).name() << llendl;
+ LL_ERRS() << "No static destroyClass() method defined for " << typeid(T).name() << LL_ENDL;
}
};
@@ -341,7 +434,7 @@ template <typename T> LLRegisterWith<LLInitClassList> LLInitClass<T>::sRegister(
template <typename T> LLRegisterWith<LLDestroyClassList> LLDestroyClass<T>::sRegister(&T::destroyClass);
// useful parameter blocks
-struct TimeIntervalParam : public LLInitParam::Choice<TimeIntervalParam>
+struct TimeIntervalParam : public LLInitParam::ChoiceBlock<TimeIntervalParam>
{
Alternative<F32> seconds;
Alternative<S32> frames;
@@ -361,20 +454,15 @@ public:
const std::string& comment = "Declared In Code")
: LLCachedControl<T>(LLUI::getControlControlGroup(name), name, default_value, comment)
{}
-
- // This constructor will signal an error if the control doesn't exist in the control group
- LLUICachedControl(const std::string& name)
- : LLCachedControl<T>(LLUI::getControlControlGroup(name), name)
- {}
};
namespace LLInitParam
{
template<>
- class TypedParam<LLRect>
- : public BlockValue<LLRect>
+ class ParamValue<LLRect>
+ : public CustomParamValue<LLRect>
{
- typedef BlockValue<LLRect> super_t;
+ typedef CustomParamValue<LLRect> super_t;
public:
Optional<S32> left,
top,
@@ -383,62 +471,43 @@ namespace LLInitParam
width,
height;
- TypedParam(BlockDescriptor& descriptor, const char* name, const LLRect& value, ParamDescriptor::validation_func_t func, S32 min_count, S32 max_count);
+ ParamValue(const LLRect& value);
- void setValueFromBlock() const;
- void setBlockFromValue();
+ void updateValueFromBlock();
+ void updateBlockFromValue(bool make_block_authoritative);
};
template<>
- struct TypeValues<LLUIColor> : public TypeValuesHelper<LLUIColor>
+ class ParamValue<LLUIColor>
+ : public CustomParamValue<LLUIColor>
{
- static void declareValues();
- };
+ typedef CustomParamValue<LLUIColor> super_t;
- template<>
- class TypedParam<LLUIColor>
- : public BlockValue<LLUIColor>
- {
- typedef BlockValue<LLUIColor> super_t;
public:
- Optional<F32> red,
- green,
- blue,
- alpha;
- Optional<std::string> control;
-
- 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();
+ Optional<F32> red,
+ green,
+ blue,
+ alpha;
+ Optional<std::string> control;
+
+ ParamValue(const LLUIColor& color);
+ void updateValueFromBlock();
+ void updateBlockFromValue(bool make_block_authoritative);
};
- // 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*>
+ class ParamValue<const LLFontGL*>
+ : public CustomParamValue<const LLFontGL* >
{
- typedef BlockValue<const LLFontGL*> super_t;
+ typedef CustomParamValue<const LLFontGL*> super_t;
public:
Optional<std::string> name,
size,
style;
- TypedParam(BlockDescriptor& descriptor, const char* name, const LLFontGL* const value, ParamDescriptor::validation_func_t func, S32 min_count, S32 max_count);
- void setValueFromBlock() const;
- void setBlockFromValue();
+ ParamValue(const LLFontGL* value);
+ void updateValueFromBlock();
+ void updateBlockFromValue(bool make_block_authoritative);
};
template<>
@@ -467,17 +536,17 @@ namespace LLInitParam
template<>
- class TypedParam<LLCoordGL>
- : public BlockValue<LLCoordGL>
+ class ParamValue<LLCoordGL>
+ : public CustomParamValue<LLCoordGL>
{
- typedef BlockValue<LLCoordGL> super_t;
+ typedef CustomParamValue<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();
+ ParamValue(const LLCoordGL& val);
+ void updateValueFromBlock();
+ void updateBlockFromValue(bool make_block_authoritative);
};
}
diff --git a/indra/llui/lluicolor.cpp b/indra/llui/lluicolor.cpp
new file mode 100755
index 0000000000..f9bb80f8c5
--- /dev/null
+++ b/indra/llui/lluicolor.cpp
@@ -0,0 +1,87 @@
+/**
+ * @file lluicolor.cpp
+ * @brief brief LLUIColor class implementation file
+ *
+ * $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 "lluicolor.h"
+
+LLUIColor::LLUIColor()
+ :mColorPtr(NULL)
+{
+}
+
+
+LLUIColor::LLUIColor(const LLColor4& color)
+: mColor(color),
+ mColorPtr(NULL)
+{
+}
+
+LLUIColor::LLUIColor(const LLUIColor* color)
+: mColorPtr(color)
+{
+}
+
+void LLUIColor::set(const LLColor4& color)
+{
+ mColor = color;
+ mColorPtr = NULL;
+}
+
+void LLUIColor::set(const LLUIColor* color)
+{
+ mColorPtr = color;
+}
+
+const LLColor4& LLUIColor::get() const
+{
+ return (mColorPtr == NULL ? mColor : mColorPtr->get());
+}
+
+LLUIColor::operator const LLColor4& () const
+{
+ return get();
+}
+
+const LLColor4& LLUIColor::operator()() const
+{
+ return get();
+}
+
+bool LLUIColor::isReference() const
+{
+ return mColorPtr != NULL;
+}
+
+namespace LLInitParam
+{
+ // used to detect equivalence with default values on export
+ bool ParamCompare<LLUIColor, false>::equals(const LLUIColor &a, const LLUIColor &b)
+ {
+ // do not detect value equivalence, treat pointers to colors as distinct from color values
+ return (a.mColorPtr == NULL && b.mColorPtr == NULL ? a.mColor == b.mColor : a.mColorPtr == b.mColorPtr);
+ }
+}
diff --git a/indra/llui/lluicolor.h b/indra/llui/lluicolor.h
new file mode 100755
index 0000000000..97ebea854a
--- /dev/null
+++ b/indra/llui/lluicolor.h
@@ -0,0 +1,71 @@
+/**
+ * @file lluicolor.h
+ * @brief brief LLUIColor class header file
+ *
+ * $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_LLUICOLOR_H_
+#define LL_LLUICOLOR_H_
+
+#include "v4color.h"
+
+namespace LLInitParam
+{
+ template<typename T, bool>
+ struct ParamCompare;
+}
+
+class LLUIColor
+{
+public:
+ LLUIColor();
+ LLUIColor(const LLColor4& color);
+ LLUIColor(const LLUIColor* color);
+
+ void set(const LLColor4& color);
+ void set(const LLUIColor* color);
+
+ const LLColor4& get() const;
+
+ operator const LLColor4& () const;
+ const LLColor4& operator()() const;
+
+ bool isReference() const;
+
+private:
+ friend struct LLInitParam::ParamCompare<LLUIColor, false>;
+
+ const LLUIColor* mColorPtr;
+ LLColor4 mColor;
+};
+
+namespace LLInitParam
+{
+ template<>
+ struct ParamCompare<LLUIColor, false>
+ {
+ static bool equals(const LLUIColor& a, const LLUIColor& b);
+ };
+}
+
+#endif
diff --git a/indra/llui/lluicolortable.cpp b/indra/llui/lluicolortable.cpp
index 9455d09cc0..244f0c6f00 100644..100755
--- a/indra/llui/lluicolortable.cpp
+++ b/indra/llui/lluicolortable.cpp
@@ -32,6 +32,7 @@
#include "llui.h"
#include "lluicolortable.h"
#include "lluictrlfactory.h"
+#include <boost/foreach.hpp>
LLUIColorTable::ColorParams::ColorParams()
: value("value"),
@@ -116,7 +117,7 @@ void LLUIColorTable::insertFromParams(const Params& p, string_color_map_t& table
unresolved_refs.erase(iter->second);
}
- llwarns << warning + ending_ref << llendl;
+ LL_WARNS() << warning + ending_ref << LL_ENDL;
break;
}
@@ -155,7 +156,7 @@ void LLUIColorTable::insertFromParams(const Params& p, string_color_map_t& table
iter != visited_refs.end();
++iter)
{
- llwarns << iter->first << " references a non-existent color" << llendl;
+ LL_WARNS() << iter->first << " references a non-existent color" << LL_ENDL;
unresolved_refs.erase(iter->second);
}
@@ -206,19 +207,12 @@ 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);
- }
-
- current_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SKIN, "colors.xml");
- if(current_filename != default_filename)
+ // pass constraint=LLDir::ALL_SKINS because we want colors.xml from every
+ // skin dir
+ BOOST_FOREACH(std::string colors_path,
+ gDirUtilp->findSkinnedFilenames(LLDir::SKINBASE, "colors.xml", LLDir::ALL_SKINS))
{
- result |= loadFromFilename(current_filename, mLoadedColors);
+ result |= loadFromFilename(colors_path, mLoadedColors);
}
std::string user_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "colors.xml");
@@ -299,13 +293,13 @@ bool LLUIColorTable::loadFromFilename(const std::string& filename, string_color_
if(!LLXMLNode::parseFile(filename, root, NULL))
{
- llwarns << "Unable to parse color file " << filename << llendl;
+ LL_WARNS() << "Unable to parse color file " << filename << LL_ENDL;
return false;
}
if(!root->hasName("colors"))
{
- llwarns << filename << " is not a valid color definition file" << llendl;
+ LL_WARNS() << filename << " is not a valid color definition file" << LL_ENDL;
return false;
}
@@ -319,7 +313,7 @@ bool LLUIColorTable::loadFromFilename(const std::string& filename, string_color_
}
else
{
- llwarns << filename << " failed to load" << llendl;
+ LL_WARNS() << filename << " failed to load" << LL_ENDL;
return false;
}
diff --git a/indra/llui/lluicolortable.h b/indra/llui/lluicolortable.h
index 76518789ec..6a7a681d57 100644..100755
--- a/indra/llui/lluicolortable.h
+++ b/indra/llui/lluicolortable.h
@@ -44,7 +44,7 @@ LOG_CLASS(LLUIColorTable);
typedef std::map<std::string, LLUIColor> string_color_map_t;
public:
- struct ColorParams : LLInitParam::Choice<ColorParams>
+ struct ColorParams : LLInitParam::ChoiceBlock<ColorParams>
{
Alternative<LLColor4> value;
Alternative<std::string> reference;
diff --git a/indra/llui/lluiconstants.h b/indra/llui/lluiconstants.h
index 1479e58c43..1479e58c43 100644..100755
--- a/indra/llui/lluiconstants.h
+++ b/indra/llui/lluiconstants.h
diff --git a/indra/llui/lluictrl.cpp b/indra/llui/lluictrl.cpp
index 0a06b5e74f..df74e113e9 100644..100755
--- a/indra/llui/lluictrl.cpp
+++ b/indra/llui/lluictrl.cpp
@@ -29,10 +29,12 @@
#define LLUICTRL_CPP
#include "lluictrl.h"
-
+#include "llviewereventrecorder.h"
#include "llfocusmgr.h"
#include "llpanel.h"
#include "lluictrlfactory.h"
+#include "lltabcontainer.h"
+#include "llaccordionctrltab.h"
static LLDefaultChildRegistry::Register<LLUICtrl> r("ui_ctrl");
@@ -68,6 +70,7 @@ LLUICtrl::ControlVisibility::ControlVisibility()
LLUICtrl::Params::Params()
: tab_stop("tab_stop", true),
chrome("chrome", false),
+ requests_front("requests_front", false),
label("label"),
initial_value("value"),
init_callback("init_callback"),
@@ -96,9 +99,10 @@ const LLUICtrl::Params& LLUICtrl::getDefaultParams()
LLUICtrl::LLUICtrl(const LLUICtrl::Params& p, const LLViewModelPtr& viewmodel)
: LLView(p),
- mTentative(FALSE),
mIsChrome(FALSE),
+ mRequestsFront(p.requests_front),
mTabStop(FALSE),
+ mTentative(FALSE),
mViewModel(viewmodel),
mControlVariable(NULL),
mEnabledControlVariable(NULL),
@@ -116,13 +120,15 @@ LLUICtrl::LLUICtrl(const LLUICtrl::Params& p, const LLViewModelPtr& viewmodel)
mDoubleClickSignal(NULL),
mTransparencyType(TT_DEFAULT)
{
- mUICtrlHandle.bind(this);
+ claimMem(viewmodel.get());
}
void LLUICtrl::initFromParams(const Params& p)
{
LLView::initFromParams(p);
+ mRequestsFront = p.requests_front;
+
setIsChrome(p.chrome);
setControlName(p.control_name);
if(p.enabled_controls.isProvided())
@@ -208,7 +214,7 @@ LLUICtrl::~LLUICtrl()
if( gFocusMgr.getTopCtrl() == this )
{
- llwarns << "UI Control holding top ctrl deleted: " << getName() << ". Top view removed." << llendl;
+ LL_WARNS() << "UI Control holding top ctrl deleted: " << getName() << ". Top view removed." << LL_ENDL;
gFocusMgr.removeTopCtrlWithoutCallback( this );
}
@@ -254,7 +260,7 @@ LLUICtrl::commit_signal_t::slot_type LLUICtrl::initCommitCallback(const CommitCa
}
else if (!function_name.empty())
{
- llwarns << "No callback found for: '" << function_name << "' in control: " << getName() << llendl;
+ LL_WARNS() << "No callback found for: '" << function_name << "' in control: " << getName() << LL_ENDL;
}
}
return default_commit_handler;
@@ -305,22 +311,40 @@ void LLUICtrl::onMouseLeave(S32 x, S32 y, MASK mask)
//virtual
BOOL LLUICtrl::handleMouseDown(S32 x, S32 y, MASK mask)
{
+
+ LL_DEBUGS() << "LLUICtrl::handleMouseDown calling LLView)'s handleMouseUp (first initialized xui to: " << getPathname() << " )" << LL_ENDL;
+
BOOL handled = LLView::handleMouseDown(x,y,mask);
+
if (mMouseDownSignal)
{
(*mMouseDownSignal)(this,x,y,mask);
}
+ LL_DEBUGS() << "LLUICtrl::handleMousedown - handled is returning as: " << handled << " " << LL_ENDL;
+
+ if (handled) {
+ LLViewerEventRecorder::instance().updateMouseEventInfo(x,y,-56,-56,getPathname());
+ }
return handled;
}
//virtual
BOOL LLUICtrl::handleMouseUp(S32 x, S32 y, MASK mask)
{
+
+ LL_DEBUGS() << "LLUICtrl::handleMouseUp calling LLView)'s handleMouseUp (first initialized xui to: " << getPathname() << " )" << LL_ENDL;
+
BOOL handled = LLView::handleMouseUp(x,y,mask);
+ if (handled) {
+ LLViewerEventRecorder::instance().updateMouseEventInfo(x,y,-56,-56,getPathname());
+ }
if (mMouseUpSignal)
{
(*mMouseUpSignal)(this,x,y,mask);
}
+
+ LL_DEBUGS() << "LLUICtrl::handleMouseUp - handled for xui " << getPathname() << " - is returning as: " << handled << " " << LL_ENDL;
+
return handled;
}
@@ -403,6 +427,36 @@ LLViewModel* LLUICtrl::getViewModel() const
return mViewModel;
}
+//virtual
+BOOL LLUICtrl::postBuild()
+{
+ //
+ // Find all of the children that want to be in front and move them to the front
+ //
+
+ if (getChildCount() > 0)
+ {
+ std::vector<LLUICtrl*> childrenToMoveToFront;
+
+ for (LLView::child_list_const_iter_t child_it = beginChild(); child_it != endChild(); ++child_it)
+ {
+ LLUICtrl* uictrl = dynamic_cast<LLUICtrl*>(*child_it);
+
+ if (uictrl && uictrl->mRequestsFront)
+ {
+ childrenToMoveToFront.push_back(uictrl);
+ }
+ }
+
+ for (std::vector<LLUICtrl*>::iterator it = childrenToMoveToFront.begin(); it != childrenToMoveToFront.end(); ++it)
+ {
+ sendChildToFront(*it);
+ }
+ }
+
+ return LLView::postBuild();
+}
+
bool LLUICtrl::setControlValue(const LLSD& value)
{
if (mControlVariable)
@@ -418,7 +472,7 @@ void LLUICtrl::setControlVariable(LLControlVariable* control)
if (mControlVariable)
{
//RN: this will happen in practice, should we try to avoid it?
- //llwarns << "setControlName called twice on same control!" << llendl;
+ //LL_WARNS() << "setControlName called twice on same control!" << LL_ENDL;
mControlConnection.disconnect(); // disconnect current signal
mControlVariable = NULL;
}
@@ -426,7 +480,7 @@ void LLUICtrl::setControlVariable(LLControlVariable* control)
if (control)
{
mControlVariable = control;
- mControlConnection = mControlVariable->getSignal()->connect(boost::bind(&controlListener, _2, getUICtrlHandle(), std::string("value")));
+ mControlConnection = mControlVariable->getSignal()->connect(boost::bind(&controlListener, _2, getHandle(), std::string("value")));
setValue(mControlVariable->getValue());
}
}
@@ -457,7 +511,7 @@ void LLUICtrl::setEnabledControlVariable(LLControlVariable* control)
if (control)
{
mEnabledControlVariable = control;
- mEnabledControlConnection = mEnabledControlVariable->getSignal()->connect(boost::bind(&controlListener, _2, getUICtrlHandle(), std::string("enabled")));
+ mEnabledControlConnection = mEnabledControlVariable->getSignal()->connect(boost::bind(&controlListener, _2, getHandle(), std::string("enabled")));
setEnabled(mEnabledControlVariable->getValue().asBoolean());
}
}
@@ -472,7 +526,7 @@ void LLUICtrl::setDisabledControlVariable(LLControlVariable* control)
if (control)
{
mDisabledControlVariable = control;
- mDisabledControlConnection = mDisabledControlVariable->getSignal()->connect(boost::bind(&controlListener, _2, getUICtrlHandle(), std::string("disabled")));
+ mDisabledControlConnection = mDisabledControlVariable->getSignal()->connect(boost::bind(&controlListener, _2, getHandle(), std::string("disabled")));
setEnabled(!(mDisabledControlVariable->getValue().asBoolean()));
}
}
@@ -487,7 +541,7 @@ void LLUICtrl::setMakeVisibleControlVariable(LLControlVariable* control)
if (control)
{
mMakeVisibleControlVariable = control;
- mMakeVisibleControlConnection = mMakeVisibleControlVariable->getSignal()->connect(boost::bind(&controlListener, _2, getUICtrlHandle(), std::string("visible")));
+ mMakeVisibleControlConnection = mMakeVisibleControlVariable->getSignal()->connect(boost::bind(&controlListener, _2, getHandle(), std::string("visible")));
setVisible(mMakeVisibleControlVariable->getValue().asBoolean());
}
}
@@ -502,7 +556,7 @@ void LLUICtrl::setMakeInvisibleControlVariable(LLControlVariable* control)
if (control)
{
mMakeInvisibleControlVariable = control;
- mMakeInvisibleControlConnection = mMakeInvisibleControlVariable->getSignal()->connect(boost::bind(&controlListener, _2, getUICtrlHandle(), std::string("invisible")));
+ mMakeInvisibleControlConnection = mMakeInvisibleControlVariable->getSignal()->connect(boost::bind(&controlListener, _2, getHandle(), std::string("invisible")));
setVisible(!(mMakeInvisibleControlVariable->getValue().asBoolean()));
}
}
@@ -668,54 +722,19 @@ BOOL LLUICtrl::getIsChrome() const
}
}
-// this comparator uses the crazy disambiguating logic of LLCompareByTabOrder,
-// but to switch up the order so that children that have the default tab group come first
-// and those that are prior to the default tab group come last
-class CompareByDefaultTabGroup: public LLCompareByTabOrder
-{
-public:
- CompareByDefaultTabGroup(const LLView::child_tab_order_t& order, S32 default_tab_group):
- LLCompareByTabOrder(order),
- mDefaultTabGroup(default_tab_group) {}
-private:
- /*virtual*/ bool compareTabOrders(const LLView::tab_order_t & a, const LLView::tab_order_t & b) const
- {
- S32 ag = a.first; // tab group for a
- S32 bg = b.first; // tab group for b
- // these two ifs have the effect of moving elements prior to the default tab group to the end of the list
- // (still sorted relative to each other, though)
- if(ag < mDefaultTabGroup && bg >= mDefaultTabGroup) return false;
- if(bg < mDefaultTabGroup && ag >= mDefaultTabGroup) return true;
- return a < b; // sort correctly if they're both on the same side of the default tab group
- }
- S32 mDefaultTabGroup;
-};
-
-// Sorter for plugging into the query.
-// I'd have defined it local to the one method that uses it but that broke the VS 05 compiler. -MG
-class LLUICtrl::DefaultTabGroupFirstSorter : public LLQuerySorter, public LLSingleton<DefaultTabGroupFirstSorter>
-{
-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");
+LLTrace::BlockTimerStatHandle FTM_FOCUS_FIRST_ITEM("Focus First Item");
BOOL LLUICtrl::focusFirstItem(BOOL prefer_text_fields, BOOL focus_flash)
{
- LLFastTimer _(FTM_FOCUS_FIRST_ITEM);
+ LL_RECORD_BLOCK_TIME(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
- query.setSorter(DefaultTabGroupFirstSorter::getInstance());
+ LLViewQuery query = getTabOrderQuery();
child_list_t result = query(this);
if(result.size() > 0)
{
- LLUICtrl * ctrl = static_cast<LLUICtrl*>(result.front());
+ LLUICtrl * ctrl = static_cast<LLUICtrl*>(result.back());
if(!ctrl->hasFocus())
{
ctrl->setFocus(TRUE);
@@ -730,17 +749,20 @@ BOOL LLUICtrl::focusFirstItem(BOOL prefer_text_fields, BOOL focus_flash)
// search for text field first
if(prefer_text_fields)
{
- LLCtrlQuery query = getTabOrderQuery();
+ LLViewQuery query = getTabOrderQuery();
query.addPreFilter(LLUICtrl::LLTextInputFilter::getInstance());
child_list_t result = query(this);
if(result.size() > 0)
{
- LLUICtrl * ctrl = static_cast<LLUICtrl*>(result.front());
+ LLUICtrl * ctrl = static_cast<LLUICtrl*>(result.back());
if(!ctrl->hasFocus())
{
ctrl->setFocus(TRUE);
ctrl->onTabInto();
- gFocusMgr.triggerFocusFlash();
+ if(focus_flash)
+ {
+ gFocusMgr.triggerFocusFlash();
+ }
}
return TRUE;
}
@@ -749,58 +771,26 @@ BOOL LLUICtrl::focusFirstItem(BOOL prefer_text_fields, BOOL focus_flash)
result = getTabOrderQuery().run(this);
if(result.size() > 0)
{
- LLUICtrl * ctrl = static_cast<LLUICtrl*>(result.front());
+ LLUICtrl * ctrl = static_cast<LLUICtrl*>(result.back());
if(!ctrl->hasFocus())
{
ctrl->setFocus(TRUE);
ctrl->onTabInto();
- gFocusMgr.triggerFocusFlash();
- }
- return TRUE;
- }
- return FALSE;
-}
-
-BOOL LLUICtrl::focusLastItem(BOOL prefer_text_fields)
-{
- // search for text field first
- if(prefer_text_fields)
- {
- LLCtrlQuery query = getTabOrderQuery();
- query.addPreFilter(LLUICtrl::LLTextInputFilter::getInstance());
- child_list_t result = query(this);
- if(result.size() > 0)
- {
- LLUICtrl * ctrl = static_cast<LLUICtrl*>(result.back());
- if(!ctrl->hasFocus())
+ if(focus_flash)
{
- ctrl->setFocus(TRUE);
- ctrl->onTabInto();
gFocusMgr.triggerFocusFlash();
}
- return TRUE;
- }
- }
- // no text field found, or we don't care about text fields
- child_list_t result = getTabOrderQuery().run(this);
- if(result.size() > 0)
- {
- LLUICtrl * ctrl = static_cast<LLUICtrl*>(result.back());
- if(!ctrl->hasFocus())
- {
- ctrl->setFocus(TRUE);
- ctrl->onTabInto();
- gFocusMgr.triggerFocusFlash();
}
return TRUE;
}
return FALSE;
}
+
BOOL LLUICtrl::focusNextItem(BOOL text_fields_only)
{
// this assumes that this method is called on the focus root.
- LLCtrlQuery query = getTabOrderQuery();
+ LLViewQuery query = getTabOrderQuery();
static LLUICachedControl<bool> tab_to_text_fields_only ("TabToTextFieldsOnly", false);
if(text_fields_only || tab_to_text_fields_only)
{
@@ -813,7 +803,7 @@ BOOL LLUICtrl::focusNextItem(BOOL text_fields_only)
BOOL LLUICtrl::focusPrevItem(BOOL text_fields_only)
{
// this assumes that this method is called on the focus root.
- LLCtrlQuery query = getTabOrderQuery();
+ LLViewQuery query = getTabOrderQuery();
static LLUICachedControl<bool> tab_to_text_fields_only ("TabToTextFieldsOnly", false);
if(text_fields_only || tab_to_text_fields_only)
{
@@ -870,8 +860,26 @@ bool LLUICtrl::findHelpTopic(std::string& help_topic_out)
if (panel)
{
+
+ LLView *child;
+ LLPanel *subpanel = NULL;
+
// does the panel have a sub-panel with a help topic?
- LLPanel *subpanel = panel->childGetVisiblePanelWithHelp();
+ bfs_tree_iterator_t it = beginTreeBFS();
+ // skip ourselves
+ ++it;
+ for (; it != endTreeBFS(); ++it)
+ {
+ child = *it;
+ // do we have a panel with a help topic?
+ LLPanel *panel = dynamic_cast<LLPanel *>(child);
+ if (panel && panel->isInVisibleChain() && !panel->getHelpTopic().empty())
+ {
+ subpanel = panel;
+ break;
+ }
+ }
+
if (subpanel)
{
help_topic_out = subpanel->getHelpTopic();
@@ -879,10 +887,41 @@ bool LLUICtrl::findHelpTopic(std::string& help_topic_out)
}
// does the panel have an active tab with a help topic?
- LLPanel *tab = panel->childGetVisibleTabWithHelp();
- if (tab)
+ LLPanel *tab_panel = NULL;
+
+ it = beginTreeBFS();
+ // skip ourselves
+ ++it;
+ for (; it != endTreeBFS(); ++it)
{
- help_topic_out = tab->getHelpTopic();
+ 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())
+ {
+ tab_panel = curTabPanel;
+ break;
+ }
+ }
+
+ if (tab_panel)
+ {
+ help_topic_out = tab_panel->getHelpTopic();
return true; // success (tab)
}
@@ -908,6 +947,8 @@ boost::signals2::connection LLUICtrl::setCommitCallback( boost::function<void (L
boost::signals2::connection LLUICtrl::setValidateBeforeCommit( boost::function<bool (const LLSD& data)> cb )
{
if (!mValidateSignal) mValidateSignal = new enable_signal_t();
+ claimMem(mValidateSignal);
+
return mValidateSignal->connect(boost::bind(cb, _2));
}
@@ -958,56 +999,90 @@ void LLUICtrl::setTransparencyType(ETypeTransparency type)
mTransparencyType = type;
}
+boost::signals2::connection LLUICtrl::setCommitCallback(const CommitCallbackParam& cb)
+{
+ return setCommitCallback(initCommitCallback(cb));
+}
+
+boost::signals2::connection LLUICtrl::setValidateCallback(const EnableCallbackParam& cb)
+{
+ return setValidateCallback(initEnableCallback(cb));
+}
+
boost::signals2::connection LLUICtrl::setCommitCallback( const commit_signal_t::slot_type& cb )
{
if (!mCommitSignal) mCommitSignal = new commit_signal_t();
+ claimMem(mCommitSignal);
+
return mCommitSignal->connect(cb);
}
boost::signals2::connection LLUICtrl::setValidateCallback( const enable_signal_t::slot_type& cb )
{
if (!mValidateSignal) mValidateSignal = new enable_signal_t();
+ claimMem(mValidateSignal);
+
return mValidateSignal->connect(cb);
}
boost::signals2::connection LLUICtrl::setMouseEnterCallback( const commit_signal_t::slot_type& cb )
{
if (!mMouseEnterSignal) mMouseEnterSignal = new commit_signal_t();
+ claimMem(mMouseEnterSignal);
+
return mMouseEnterSignal->connect(cb);
}
boost::signals2::connection LLUICtrl::setMouseLeaveCallback( const commit_signal_t::slot_type& cb )
{
if (!mMouseLeaveSignal) mMouseLeaveSignal = new commit_signal_t();
+ claimMem(mMouseLeaveSignal);
+
return mMouseLeaveSignal->connect(cb);
}
boost::signals2::connection LLUICtrl::setMouseDownCallback( const mouse_signal_t::slot_type& cb )
{
if (!mMouseDownSignal) mMouseDownSignal = new mouse_signal_t();
+ claimMem(mMouseDownSignal);
+
return mMouseDownSignal->connect(cb);
}
boost::signals2::connection LLUICtrl::setMouseUpCallback( const mouse_signal_t::slot_type& cb )
{
if (!mMouseUpSignal) mMouseUpSignal = new mouse_signal_t();
+ claimMem(mMouseUpSignal);
+
return mMouseUpSignal->connect(cb);
}
boost::signals2::connection LLUICtrl::setRightMouseDownCallback( const mouse_signal_t::slot_type& cb )
{
if (!mRightMouseDownSignal) mRightMouseDownSignal = new mouse_signal_t();
+ claimMem(mRightMouseDownSignal);
+
return mRightMouseDownSignal->connect(cb);
}
boost::signals2::connection LLUICtrl::setRightMouseUpCallback( const mouse_signal_t::slot_type& cb )
{
if (!mRightMouseUpSignal) mRightMouseUpSignal = new mouse_signal_t();
+ claimMem(mRightMouseUpSignal);
+
return mRightMouseUpSignal->connect(cb);
}
boost::signals2::connection LLUICtrl::setDoubleClickCallback( const mouse_signal_t::slot_type& cb )
{
if (!mDoubleClickSignal) mDoubleClickSignal = new mouse_signal_t();
+ claimMem(mDoubleClickSignal);
+
return mDoubleClickSignal->connect(cb);
}
+
+void LLUICtrl::addInfo(LLSD & info)
+{
+ LLView::addInfo(info);
+ info["value"] = getValue();
+}
diff --git a/indra/llui/lluictrl.h b/indra/llui/lluictrl.h
index b37e9f6b1b..99553ee0d2 100644..100755
--- a/indra/llui/lluictrl.h
+++ b/indra/llui/lluictrl.h
@@ -76,14 +76,14 @@ public:
Optional<enable_callback_t> function;
};
- struct EnableControls : public LLInitParam::Choice<EnableControls>
+ struct EnableControls : public LLInitParam::ChoiceBlock<EnableControls>
{
Alternative<std::string> enabled;
Alternative<std::string> disabled;
EnableControls();
};
- struct ControlVisibility : public LLInitParam::Choice<ControlVisibility>
+ struct ControlVisibility : public LLInitParam::ChoiceBlock<ControlVisibility>
{
Alternative<std::string> visible;
Alternative<std::string> invisible;
@@ -94,7 +94,8 @@ public:
{
Optional<std::string> label;
Optional<bool> tab_stop,
- chrome;
+ chrome,
+ requests_front;
Optional<LLSD> initial_value;
Optional<CommitCallbackParam> init_callback,
@@ -143,6 +144,8 @@ protected:
virtual LLViewModel* getViewModel() const;
// We shouldn't ever need to set this directly
//virtual void setViewModel(const LLViewModelPtr&);
+
+ virtual BOOL postBuild();
public:
// LLView interface
@@ -217,10 +220,9 @@ public:
BOOL focusNextItem(BOOL text_entry_only);
BOOL focusPrevItem(BOOL text_entry_only);
BOOL focusFirstItem(BOOL prefer_text_fields = FALSE, BOOL focus_flash = TRUE );
- BOOL focusLastItem(BOOL prefer_text_fields = FALSE);
// Non Virtuals
- LLHandle<LLUICtrl> getUICtrlHandle() const { return mUICtrlHandle; }
+ LLHandle<LLUICtrl> getHandle() const { return getDerivedHandle<LLUICtrl>(); }
BOOL getIsChrome() const;
void setTabStop( BOOL b );
@@ -232,6 +234,9 @@ public:
// topic then put in help_topic_out
bool findHelpTopic(std::string& help_topic_out);
+ boost::signals2::connection setCommitCallback(const CommitCallbackParam& cb);
+ boost::signals2::connection setValidateCallback(const EnableCallbackParam& cb);
+
boost::signals2::connection setCommitCallback( const commit_signal_t::slot_type& cb );
boost::signals2::connection setValidateCallback( const enable_signal_t::slot_type& cb );
@@ -298,17 +303,17 @@ protected:
static F32 sActiveControlTransparency;
static F32 sInactiveControlTransparency;
-
+
+ virtual void addInfo(LLSD & info);
+
private:
- BOOL mTabStop;
BOOL mIsChrome;
+ BOOL mRequestsFront;
+ BOOL mTabStop;
BOOL mTentative;
- LLRootHandle<LLUICtrl> mUICtrlHandle;
ETypeTransparency mTransparencyType;
-
- class DefaultTabGroupFirstSorter;
};
// Build time optimization, generate once in .cpp file
diff --git a/indra/llui/lluictrlfactory.cpp b/indra/llui/lluictrlfactory.cpp
index 5de96f9d48..4cc7da1267 100644..100755
--- a/indra/llui/lluictrlfactory.cpp
+++ b/indra/llui/lluictrlfactory.cpp
@@ -44,9 +44,9 @@
// this library includes
#include "llpanel.h"
-LLFastTimer::DeclareTimer FTM_WIDGET_CONSTRUCTION("Widget Construction");
-LLFastTimer::DeclareTimer FTM_INIT_FROM_PARAMS("Widget InitFromParams");
-LLFastTimer::DeclareTimer FTM_WIDGET_SETUP("Widget Setup");
+LLTrace::BlockTimerStatHandle FTM_WIDGET_CONSTRUCTION("Widget Construction");
+LLTrace::BlockTimerStatHandle FTM_INIT_FROM_PARAMS("Widget InitFromParams");
+LLTrace::BlockTimerStatHandle FTM_WIDGET_SETUP("Widget Setup");
//-----------------------------------------------------------------------------
@@ -90,10 +90,12 @@ LLUICtrlFactory::~LLUICtrlFactory()
void LLUICtrlFactory::loadWidgetTemplate(const std::string& widget_tag, LLInitParam::BaseBlock& block)
{
- std::string filename = std::string("widgets") + gDirUtilp->getDirDelimiter() + widget_tag + ".xml";
+ std::string filename = gDirUtilp->add("widgets", widget_tag + ".xml");
LLXMLNodePtr root_node;
- std::string full_filename = gDirUtilp->findSkinnedFilename(LLUI::getXUIPaths().front(), filename);
+ // Here we're looking for the "en" version, the default-language version
+ // of the file, rather than the localized version.
+ std::string full_filename = gDirUtilp->findSkinnedFilenameBaseLang(LLDir::XUI, filename);
if (!full_filename.empty())
{
LLUICtrlFactory::instance().pushFileName(full_filename);
@@ -103,12 +105,12 @@ void LLUICtrlFactory::loadWidgetTemplate(const std::string& widget_tag, LLInitPa
}
}
-static LLFastTimer::DeclareTimer FTM_CREATE_CHILDREN("Create XUI Children");
+static LLTrace::BlockTimerStatHandle FTM_CREATE_CHILDREN("Create XUI Children");
//static
void LLUICtrlFactory::createChildren(LLView* viewp, LLXMLNodePtr node, const widget_registry_t& registry, LLXMLNodePtr output_node)
{
- LLFastTimer ft(FTM_CREATE_CHILDREN);
+ LL_RECORD_BLOCK_TIME(FTM_CREATE_CHILDREN);
if (node.isNull()) return;
for (LLXMLNodePtr child_node = node->getFirstChild(); child_node.notNull(); child_node = child_node->getNextSibling())
@@ -129,11 +131,11 @@ void LLUICtrlFactory::createChildren(LLView* viewp, LLXMLNodePtr node, const wid
// 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;
+ LL_WARNS() << child_name << " is not a valid child of " << node->getName()->mString << LL_ENDL;
}
else
{
- llwarns << "Could not create widget named " << child_node->getName()->mString << llendl;
+ LL_WARNS() << "Could not create widget named " << child_node->getName()->mString << LL_ENDL;
}
}
@@ -145,34 +147,27 @@ void LLUICtrlFactory::createChildren(LLView* viewp, LLXMLNodePtr node, const wid
}
-static LLFastTimer::DeclareTimer FTM_XML_PARSE("XML Reading/Parsing");
+static LLTrace::BlockTimerStatHandle FTM_XML_PARSE("XML Reading/Parsing");
//-----------------------------------------------------------------------------
// getLayeredXMLNode()
//-----------------------------------------------------------------------------
-bool LLUICtrlFactory::getLayeredXMLNode(const std::string &xui_filename, LLXMLNodePtr& root)
+bool LLUICtrlFactory::getLayeredXMLNode(const std::string &xui_filename, LLXMLNodePtr& root,
+ LLDir::ESkinConstraint constraint)
{
- LLFastTimer timer(FTM_XML_PARSE);
- return LLXMLNode::getLayeredXMLNode(xui_filename, root, LLUI::getXUIPaths());
-}
-
+ LL_RECORD_BLOCK_TIME(FTM_XML_PARSE);
+ std::vector<std::string> paths =
+ gDirUtilp->findSkinnedFilenames(LLDir::XUI, xui_filename, constraint);
-//-----------------------------------------------------------------------------
-// getLocalizedXMLNode()
-//-----------------------------------------------------------------------------
-bool LLUICtrlFactory::getLocalizedXMLNode(const std::string &xui_filename, LLXMLNodePtr& root)
-{
- LLFastTimer timer(FTM_XML_PARSE);
- std::string full_filename = gDirUtilp->findSkinnedFilename(LLUI::getLocalizedSkinPath(), xui_filename);
- if (!LLXMLNode::parseFile(full_filename, root, NULL))
+ if (paths.empty())
{
- return false;
- }
- else
- {
- return true;
+ // sometimes whole path is passed in as filename
+ paths.push_back(xui_filename);
}
+
+ return LLXMLNode::getLayeredXMLNode(root, paths);
}
+
//-----------------------------------------------------------------------------
// saveToXML()
//-----------------------------------------------------------------------------
@@ -184,11 +179,11 @@ S32 LLUICtrlFactory::saveToXML(LLView* viewp, const std::string& filename)
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
-static LLFastTimer::DeclareTimer FTM_CREATE_FROM_XML("Create child widget");
+static LLTrace::BlockTimerStatHandle FTM_CREATE_FROM_XML("Create child widget");
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);
+ LL_RECORD_BLOCK_TIME(FTM_CREATE_FROM_XML);
std::string ctrl_type = node->getName()->mString;
LLStringUtil::toLower(ctrl_type);
@@ -214,13 +209,15 @@ LLView *LLUICtrlFactory::createFromXML(LLXMLNodePtr node, LLView* parent, const
std::string LLUICtrlFactory::getCurFileName()
{
- return mFileNames.empty() ? "" : gDirUtilp->getWorkingDir() + gDirUtilp->getDirDelimiter() + mFileNames.back();
+ return mFileNames.empty() ? "" : mFileNames.back();
}
void LLUICtrlFactory::pushFileName(const std::string& name)
-{
- mFileNames.push_back(gDirUtilp->findSkinnedFilename(LLUI::getSkinPath(), name));
+{
+ // Here we seem to be looking for the default language file ("en") rather
+ // than the localized one, if any.
+ mFileNames.push_back(gDirUtilp->findSkinnedFilenameBaseLang(LLDir::XUI, name));
}
void LLUICtrlFactory::popFileName()
@@ -235,14 +232,6 @@ void LLUICtrlFactory::setCtrlParent(LLView* view, LLView* parent, S32 tab_group)
parent->addChild(view, tab_group);
}
-
-// Avoid directly using LLUI and LLDir in the template code
-//static
-std::string LLUICtrlFactory::findSkinnedFilename(const std::string& filename)
-{
- return gDirUtilp->findSkinnedFilename(LLUI::getSkinPath(), filename);
-}
-
//static
void LLUICtrlFactory::copyName(LLXMLNodePtr src, LLXMLNodePtr dest)
{
@@ -258,13 +247,13 @@ const LLInitParam::BaseBlock& get_empty_param_block()
// adds a widget and its param block to various registries
//static
-void LLUICtrlFactory::registerWidget(const std::type_info* widget_type, const std::type_info* param_block_type, const std::string& tag)
+void LLUICtrlFactory::registerWidget(const std::type_info* widget_type, const std::type_info* param_block_type, const std::string& name)
{
// associate parameter block type with template .xml file
- std::string* existing_tag = LLWidgetNameRegistry::instance().getValue(param_block_type);
- if (existing_tag != NULL)
+ std::string* existing_name = LLWidgetNameRegistry::instance().getValue(param_block_type);
+ if (existing_name != NULL)
{
- if(*existing_tag != tag)
+ if(*existing_name != name)
{
std::cerr << "Duplicate entry for T::Params, try creating empty param block in derived classes that inherit T::Params" << std::endl;
// forcing crash here
@@ -273,19 +262,15 @@ void LLUICtrlFactory::registerWidget(const std::type_info* widget_type, const st
}
else
{
- // widget already registered
+ // widget already registered this name
return;
}
}
- LLWidgetNameRegistry::instance().defaultRegistrar().add(param_block_type, tag);
+
+ LLWidgetNameRegistry::instance().defaultRegistrar().add(param_block_type, name);
//FIXME: comment this in when working on schema generation
//LLWidgetTypeRegistry::instance().defaultRegistrar().add(tag, widget_type);
//LLDefaultParamBlockRegistry::instance().defaultRegistrar().add(widget_type, &get_empty_param_block<T>);
}
-//static
-const std::string* LLUICtrlFactory::getWidgetTag(const std::type_info* widget_type)
-{
- return LLWidgetNameRegistry::instance().getValue(widget_type);
-}
diff --git a/indra/llui/lluictrlfactory.h b/indra/llui/lluictrlfactory.h
index 499b97f52d..3ce39c947f 100644..100755
--- a/indra/llui/lluictrlfactory.h
+++ b/indra/llui/lluictrlfactory.h
@@ -31,18 +31,11 @@
#include "llinitparam.h"
#include "llregistry.h"
#include "llxuiparser.h"
+#include "llstl.h"
+#include "lldir.h"
class LLView;
-// sort functor for typeid maps
-struct LLCompareTypeID
-{
- bool operator()(const std::type_info* lhs, const std::type_info* rhs) const
- {
- return lhs->before(*rhs);
- }
-};
-
// lookup widget constructor funcs by widget name
template <typename DERIVED_TYPE>
class LLChildRegistry : public LLRegistrySingleton<std::string, LLWidgetCreatorFunc, DERIVED_TYPE>
@@ -71,19 +64,19 @@ protected:
// lookup widget name by type
class LLWidgetNameRegistry
-: public LLRegistrySingleton<const std::type_info*, std::string, LLWidgetNameRegistry , LLCompareTypeID>
+: public LLRegistrySingleton<const std::type_info*, std::string, LLWidgetNameRegistry>
{};
// 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>
+//: public LLRegistrySingleton<const std::type_info*, empty_param_block_func_t, LLDefaultParamBlockRegistry>
//{};
-extern LLFastTimer::DeclareTimer FTM_WIDGET_SETUP;
-extern LLFastTimer::DeclareTimer FTM_WIDGET_CONSTRUCTION;
-extern LLFastTimer::DeclareTimer FTM_INIT_FROM_PARAMS;
+extern LLTrace::BlockTimerStatHandle FTM_WIDGET_SETUP;
+extern LLTrace::BlockTimerStatHandle FTM_WIDGET_CONSTRUCTION;
+extern LLTrace::BlockTimerStatHandle FTM_INIT_FROM_PARAMS;
// Build time optimization, generate this once in .cpp file
#ifndef LLUICTRLFACTORY_CPP
@@ -104,14 +97,17 @@ private:
public:
ParamDefaults()
{
- // recursively initialize from base class param block
- ((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
- const std::string* param_block_tag = getWidgetTag(&typeid(PARAM_BLOCK));
+ // look up template file for this param block...
+ const std::string* param_block_tag = LLWidgetNameRegistry::instance().getValue(&typeid(PARAM_BLOCK));
if (param_block_tag)
- {
- LLUICtrlFactory::loadWidgetTemplate(*param_block_tag, mPrototype);
+ { // ...and if it exists, back fill values using the most specific template first
+ PARAM_BLOCK params;
+ LLUICtrlFactory::loadWidgetTemplate(*param_block_tag, params);
+ mPrototype.fillFrom(params);
}
+ // recursively fill from base class param block
+ ((typename PARAM_BLOCK::base_block_t&)mPrototype).fillFrom(ParamDefaults<typename PARAM_BLOCK::base_block_t, DUMMY>::instance().get());
+
}
const PARAM_BLOCK& get() { return mPrototype; }
@@ -136,7 +132,6 @@ public:
template<typename T>
static const typename T::Params& getDefaultParams()
{
- //#pragma message("Generating ParamDefaults")
return ParamDefaults<typename T::Params, 0>::instance().get();
}
@@ -166,42 +161,39 @@ public:
LLView* createFromXML(LLXMLNodePtr node, LLView* parent, const std::string& filename, const widget_registry_t&, LLXMLNodePtr output_node );
template<typename T>
- static T* createFromFile(const std::string &filename, LLView *parent, const widget_registry_t& registry, LLXMLNodePtr output_node = NULL)
+ static T* createFromFile(const std::string &filename, LLView *parent, const widget_registry_t& registry)
{
T* widget = NULL;
- std::string skinned_filename = findSkinnedFilename(filename);
instance().pushFileName(filename);
{
LLXMLNodePtr root_node;
- //if exporting, only load the language being exported,
- //instead of layering localized version on top of english
- if (output_node)
- {
- if (!LLUICtrlFactory::getLocalizedXMLNode(filename, root_node))
+ if (!LLUICtrlFactory::getLayeredXMLNode(filename, root_node))
{
- llwarns << "Couldn't parse XUI file: " << filename << llendl;
- goto fail;
- }
- }
- else if (!LLUICtrlFactory::getLayeredXMLNode(filename, root_node))
- {
- llwarns << "Couldn't parse XUI file: " << skinned_filename << llendl;
+ LL_WARNS() << "Couldn't parse XUI file: " << instance().getCurFileName() << LL_ENDL;
goto fail;
}
-
- LLView* view = getInstance()->createFromXML(root_node, parent, filename, registry, output_node);
+
+ LLView* view = getInstance()->createFromXML(root_node, parent, filename, registry, NULL);
if (view)
{
widget = dynamic_cast<T*>(view);
// not of right type, so delete it
if (!widget)
{
+ LL_WARNS() << "Widget in " << filename << " was of type " << typeid(view).name() << " instead of expected type " << typeid(T).name() << LL_ENDL;
+
+#if LL_DARWIN
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdelete-incomplete"
delete view;
+#pragma clang diagnostic pop
+#else
+ delete view;
+#endif
view = NULL;
}
-
}
}
fail:
@@ -219,8 +211,8 @@ fail:
static void createChildren(LLView* viewp, LLXMLNodePtr node, const widget_registry_t&, LLXMLNodePtr output_node = NULL);
- static bool getLayeredXMLNode(const std::string &filename, LLXMLNodePtr& root);
- static bool getLocalizedXMLNode(const std::string &xui_filename, LLXMLNodePtr& root);
+ static bool getLayeredXMLNode(const std::string &filename, LLXMLNodePtr& root,
+ LLDir::ESkinConstraint constraint=LLDir::CURRENT_SKIN);
private:
//NOTE: both friend declarations are necessary to keep both gcc and msvc happy
@@ -241,14 +233,14 @@ private:
if (!params.validateBlock())
{
- llwarns << getInstance()->getCurFileName() << ": Invalid parameter block for " << typeid(T).name() << llendl;
+ LL_WARNS() << getInstance()->getCurFileName() << ": Invalid parameter block for " << typeid(T).name() << LL_ENDL;
//return NULL;
}
- { LLFastTimer _(FTM_WIDGET_CONSTRUCTION);
+ { LL_RECORD_BLOCK_TIME(FTM_WIDGET_CONSTRUCTION);
widget = new T(params);
}
- { LLFastTimer _(FTM_INIT_FROM_PARAMS);
+ { LL_RECORD_BLOCK_TIME(FTM_INIT_FROM_PARAMS);
widget->initFromParams(params);
}
@@ -263,7 +255,7 @@ private:
template<typename T>
static T* defaultBuilder(LLXMLNodePtr node, LLView *parent, LLXMLNodePtr output_node)
{
- LLFastTimer timer(FTM_WIDGET_SETUP);
+ LL_RECORD_BLOCK_TIME(FTM_WIDGET_SETUP);
typename T::Params params(getDefaultParams<T>());
@@ -275,10 +267,8 @@ private:
// We always want to output top-left coordinates
typename T::Params output_params(params);
T::setupParamsForExport(output_params, parent);
- // Export only the differences between this any default params
- typename T::Params default_params(getDefaultParams<T>());
copyName(node, output_node);
- parser.writeXUI(output_node, output_params, &default_params);
+ parser.writeXUI(output_node, output_params, LLInitParam::default_parse_rules(), &getDefaultParams<T>());
}
// Apply layout transformations, usually munging rect
@@ -300,14 +290,9 @@ private:
}
- static const std::string* getWidgetTag(const std::type_info* widget_type);
-
// this exists to get around dependency on llview
static void setCtrlParent(LLView* view, LLView* parent, S32 tab_group);
- // Avoid directly using LLUI and LLDir in the template code
- static std::string findSkinnedFilename(const std::string& filename);
-
class LLPanel* mDummyPanel;
std::vector<std::string> mFileNames;
};
diff --git a/indra/llui/lluifwd.h b/indra/llui/lluifwd.h
index a68629a091..a68629a091 100644..100755
--- a/indra/llui/lluifwd.h
+++ b/indra/llui/lluifwd.h
diff --git a/indra/llui/lluiimage.cpp b/indra/llui/lluiimage.cpp
deleted file mode 100644
index 1ffad4806e..0000000000
--- a/indra/llui/lluiimage.cpp
+++ /dev/null
@@ -1,199 +0,0 @@
-/**
- * @file lluiimage.cpp
- * @brief UI implementation
- *
- * $LicenseInfo:firstyear=2007&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-// Utilities functions the user interface needs
-
-//#include "llviewerprecompiledheaders.h"
-#include "linden_common.h"
-
-// Project includes
-#include "lluiimage.h"
-#include "llui.h"
-
-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)
-{
- mClipRegion = region;
- mNoClip = mClipRegion.mLeft == 0.f
- && mClipRegion.mRight == 1.f
- && mClipRegion.mBottom == 0.f
- && mClipRegion.mTop == 1.f;
-}
-
-void LLUIImage::setScaleRegion(const LLRectf& region)
-{
- mScaleRegion = region;
- mUniformScaling = mScaleRegion.mLeft == 0.f
- && mScaleRegion.mRight == 1.f
- && mScaleRegion.mBottom == 0.f
- && mScaleRegion.mTop == 1.f;
-}
-
-//TODO: move drawing implementation inside class
-void LLUIImage::draw(S32 x, S32 y, const LLColor4& color) const
-{
- 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
-{
- if (mUniformScaling)
- {
- gl_draw_scaled_image(x, y, width, height, mImage, color, mClipRegion);
- }
- else
- {
- gl_draw_scaled_image_with_border(
- x, y,
- width, height,
- mImage,
- color,
- FALSE,
- mClipRegion,
- mScaleRegion);
- }
-}
-
-void LLUIImage::drawSolid(S32 x, S32 y, S32 width, S32 height, const LLColor4& color) const
-{
- gl_draw_scaled_image_with_border(
- x, y,
- width, height,
- mImage,
- color,
- TRUE,
- mClipRegion,
- mScaleRegion);
-}
-
-void LLUIImage::drawBorder(S32 x, S32 y, S32 width, S32 height, const LLColor4& color, S32 border_width) const
-{
- LLRect border_rect;
- border_rect.setOriginAndSize(x, y, width, height);
- border_rect.stretch(border_width, border_width);
- drawSolid(border_rect, color);
-}
-
-S32 LLUIImage::getWidth() const
-{
- // return clipped dimensions of actual image area
- return llround((F32)mImage->getWidth(0) * mClipRegion.getWidth());
-}
-
-S32 LLUIImage::getHeight() const
-{
- // return clipped dimensions of actual image area
- return llround((F32)mImage->getHeight(0) * mClipRegion.getHeight());
-}
-
-S32 LLUIImage::getTextureWidth() const
-{
- return mImage->getWidth(0);
-}
-
-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
-{
- 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)
- {
- mData.mValue = imagep;
- }
- }
-
- void TypedParam<LLUIImage*>::setBlockFromValue()
- {
- if (mData.mValue == NULL)
- {
- name.set("none", false);
- }
- else
- {
- name.set(mData.mValue->getName(), false);
- }
- }
-
-
- bool ParamCompare<LLUIImage*, false>::equals(
- LLUIImage* const &a,
- LLUIImage* const &b)
- {
- // force all LLUIImages for XML UI export to be "non-default"
- if (!a && !b)
- return false;
- else
- return (a == b);
- }
-}
-
diff --git a/indra/llui/lluiimage.h b/indra/llui/lluiimage.h
deleted file mode 100644
index 38107c112d..0000000000
--- a/indra/llui/lluiimage.h
+++ /dev/null
@@ -1,123 +0,0 @@
-/**
- * @file lluiimage.h
- * @brief wrapper for images used in the UI that handles smart scaling, etc.
- *
- * $LicenseInfo:firstyear=2007&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLUIIMAGE_H
-#define LL_LLUIIMAGE_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:
- 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<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;
- void draw(const LLRect& rect, const LLColor4& color = UI_VERTEX_COLOR) const { draw(rect.mLeft, rect.mBottom, rect.getWidth(), rect.getHeight(), color); }
-
- 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, 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, getWidth(), getHeight(), color, border_width); }
-
- const std::string& getName() const { return mName; }
-
- virtual S32 getWidth() const;
- virtual S32 getHeight() const;
-
- // returns dimensions of underlying textures, which might not be equal to ui image portion
- 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<LLTexture> mImage;
- BOOL mUniformScaling;
- BOOL mNoClip;
-};
-
-namespace LLInitParam
-{
- template<>
- class TypedParam<LLUIImage*, TypeValues<LLUIImage*>, false>
- : public BlockValue<LLUIImage*>
- {
- typedef boost::add_reference<boost::add_const<LLUIImage*>::type>::type T_const_ref;
- typedef BlockValue<LLUIImage*> super_t;
- public:
- Optional<std::string> name;
-
- TypedParam(BlockDescriptor& descriptor, const char* name, super_t::value_assignment_t value, ParamDescriptor::validation_func_t func, S32 min_count, S32 max_count)
- : super_t(descriptor, name, value, func, min_count, max_count)
- {
- setBlockFromValue();
- }
-
- void setValueFromBlock() const;
- void setBlockFromValue();
- };
-
- // Need custom comparison function for our test app, which only loads
- // LLUIImage* as NULL.
- template<>
- struct ParamCompare<LLUIImage*, false>
- {
- static bool equals(LLUIImage* const &a, LLUIImage* const &b);
- };
-}
-
-typedef LLPointer<LLUIImage> LLUIImagePtr;
-#endif
diff --git a/indra/llui/lluistring.cpp b/indra/llui/lluistring.cpp
index ac69d3bf85..98d0c215e6 100644..100755
--- a/indra/llui/lluistring.cpp
+++ b/indra/llui/lluistring.cpp
@@ -26,10 +26,12 @@
#include "linden_common.h"
#include "lluistring.h"
+
+#include "llfasttimer.h"
#include "llsd.h"
#include "lltrans.h"
-LLFastTimer::DeclareTimer FTM_UI_STRING("UI String");
+LLTrace::BlockTimerStatHandle FTM_UI_STRING("UI String");
LLUIString::LLUIString(const std::string& instring, const LLStringUtil::format_map_t& args)
@@ -54,7 +56,7 @@ void LLUIString::setArgList(const LLStringUtil::format_map_t& args)
void LLUIString::setArgs(const LLSD& sd)
{
- LLFastTimer timer(FTM_UI_STRING);
+ LL_RECORD_BLOCK_TIME(FTM_UI_STRING);
if (!sd.isMap()) return;
for(LLSD::map_const_iterator sd_it = sd.beginMap();
@@ -117,7 +119,7 @@ void LLUIString::updateResult() const
{
mNeedsResult = false;
- LLFastTimer timer(FTM_UI_STRING);
+ LL_RECORD_BLOCK_TIME(FTM_UI_STRING);
// optimize for empty strings (don't attempt string replacement)
if (mOrig.empty())
@@ -128,17 +130,13 @@ void LLUIString::updateResult() const
}
mResult = mOrig;
- // get the defailt args + local args
- if (!mArgs || mArgs->empty())
- {
- LLStringUtil::format(mResult, LLTrans::getDefaultArgs());
- }
- else
+ // get the default args + local args
+ LLStringUtil::format_map_t combined_args = LLTrans::getDefaultArgs();
+ if (mArgs && !mArgs->empty())
{
- LLStringUtil::format_map_t combined_args = LLTrans::getDefaultArgs();
combined_args.insert(mArgs->begin(), mArgs->end());
- LLStringUtil::format(mResult, combined_args);
}
+ LLStringUtil::format(mResult, combined_args);
}
void LLUIString::updateWResult() const
diff --git a/indra/llui/lluistring.h b/indra/llui/lluistring.h
index cb40c85582..07e02de6d8 100644..100755
--- a/indra/llui/lluistring.h
+++ b/indra/llui/lluistring.h
@@ -42,12 +42,12 @@
// LLUIString mMessage("Welcome [USERNAME] to [SECONDLIFE]!");
// mMessage.setArg("[USERNAME]", "Steve");
// mMessage.setArg("[SECONDLIFE]", "Second Life");
-// llinfos << mMessage.getString() << llendl; // outputs "Welcome Steve to Second Life"
+// LL_INFOS() << mMessage.getString() << LL_ENDL; // outputs "Welcome Steve to Second Life"
// mMessage.setArg("[USERNAME]", "Joe");
-// llinfos << mMessage.getString() << llendl; // outputs "Welcome Joe to Second Life"
+// LL_INFOS() << mMessage.getString() << LL_ENDL; // outputs "Welcome Joe to Second Life"
// mMessage = "Bienvenido a la [SECONDLIFE] [USERNAME]"
// mMessage.setArg("[SECONDLIFE]", "Segunda Vida");
-// llinfos << mMessage.getString() << llendl; // outputs "Bienvenido a la Segunda Vida Joe"
+// LL_INFOS() << mMessage.getString() << LL_ENDL; // outputs "Bienvenido a la Segunda Vida Joe"
// Implementation Notes:
// Attempting to have operator[](const std::string& s) return mArgs[s] fails because we have
diff --git a/indra/llui/llundo.cpp b/indra/llui/llundo.cpp
index 06b0514223..7c4c183a30 100644..100755
--- a/indra/llui/llundo.cpp
+++ b/indra/llui/llundo.cpp
@@ -51,7 +51,7 @@ LLUndoBuffer::LLUndoBuffer( LLUndoAction (*create_func()), S32 initial_count )
mActions[i] = create_func();
if (!mActions[i])
{
- llerrs << "Unable to create action for undo buffer" << llendl;
+ LL_ERRS() << "Unable to create action for undo buffer" << LL_ENDL;
}
}
}
diff --git a/indra/llui/llundo.h b/indra/llui/llundo.h
index a6da550126..a6da550126 100644..100755
--- a/indra/llui/llundo.h
+++ b/indra/llui/llundo.h
diff --git a/indra/llui/llurlaction.cpp b/indra/llui/llurlaction.cpp
index 42b779bd28..12537d9dd1 100644..100755
--- a/indra/llui/llurlaction.cpp
+++ b/indra/llui/llurlaction.cpp
@@ -24,7 +24,6 @@
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
-
#include "linden_common.h"
#include "llurlaction.h"
@@ -32,29 +31,30 @@
#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;
+LLUrlAction::url_callback_t LLUrlAction::sOpenURLCallback;
+LLUrlAction::url_callback_t LLUrlAction::sOpenURLInternalCallback;
+LLUrlAction::url_callback_t LLUrlAction::sOpenURLExternalCallback;
+LLUrlAction::execute_url_callback_t LLUrlAction::sExecuteSLURLCallback;
-void LLUrlAction::setOpenURLCallback(void (*cb) (const std::string& url))
+void LLUrlAction::setOpenURLCallback(url_callback_t cb)
{
sOpenURLCallback = cb;
}
-void LLUrlAction::setOpenURLInternalCallback(void (*cb) (const std::string& url))
+void LLUrlAction::setOpenURLInternalCallback(url_callback_t cb)
{
sOpenURLInternalCallback = cb;
}
-void LLUrlAction::setOpenURLExternalCallback(void (*cb) (const std::string& url))
+void LLUrlAction::setOpenURLExternalCallback(url_callback_t cb)
{
sOpenURLExternalCallback = cb;
}
-void LLUrlAction::setExecuteSLURLCallback(bool (*cb) (const std::string& url))
+void LLUrlAction::setExecuteSLURLCallback(execute_url_callback_t cb)
{
sExecuteSLURLCallback = cb;
}
@@ -63,7 +63,7 @@ void LLUrlAction::openURL(std::string url)
{
if (sOpenURLCallback)
{
- (*sOpenURLCallback)(url);
+ sOpenURLCallback(url);
}
}
@@ -71,7 +71,7 @@ void LLUrlAction::openURLInternal(std::string url)
{
if (sOpenURLInternalCallback)
{
- (*sOpenURLInternalCallback)(url);
+ sOpenURLInternalCallback(url);
}
}
@@ -79,7 +79,7 @@ void LLUrlAction::openURLExternal(std::string url)
{
if (sOpenURLExternalCallback)
{
- (*sOpenURLExternalCallback)(url);
+ sOpenURLExternalCallback(url);
}
}
@@ -87,18 +87,18 @@ void LLUrlAction::executeSLURL(std::string url)
{
if (sExecuteSLURLCallback)
{
- (*sExecuteSLURLCallback)(url);
+ sExecuteSLURLCallback(url ,true);
}
}
-void LLUrlAction::clickAction(std::string url)
+void LLUrlAction::clickAction(std::string url, bool trusted_content)
{
// Try to handle as SLURL first, then http Url
- if ( (sExecuteSLURLCallback) && !(*sExecuteSLURLCallback)(url) )
+ if ( (sExecuteSLURLCallback) && !sExecuteSLURLCallback(url, trusted_content) )
{
if (sOpenURLCallback)
{
- (*sOpenURLCallback)(url);
+ sOpenURLCallback(url);
}
}
}
@@ -157,3 +157,76 @@ void LLUrlAction::showProfile(std::string url)
}
}
}
+
+std::string LLUrlAction::getUserID(std::string url)
+{
+ LLURI uri(url);
+ LLSD path_array = uri.pathArray();
+ std::string id_str;
+ if (path_array.size() == 4)
+ {
+ id_str = path_array.get(2).asString();
+ }
+ return id_str;
+}
+
+std::string LLUrlAction::getObjectId(std::string url)
+{
+ LLURI uri(url);
+ LLSD path_array = uri.pathArray();
+ std::string id_str;
+ if (path_array.size() >= 3)
+ {
+ id_str = path_array.get(2).asString();
+ }
+ return id_str;
+}
+
+std::string LLUrlAction::getObjectName(std::string url)
+{
+ LLURI uri(url);
+ LLSD query_map = uri.queryMap();
+ std::string name;
+ if (query_map.has("name"))
+ {
+ name = query_map["name"].asString();
+ }
+ return name;
+}
+
+void LLUrlAction::sendIM(std::string url)
+{
+ std::string id_str = getUserID(url);
+ if (LLUUID::validate(id_str))
+ {
+ executeSLURL("secondlife:///app/agent/" + id_str + "/im");
+ }
+}
+
+void LLUrlAction::addFriend(std::string url)
+{
+ std::string id_str = getUserID(url);
+ if (LLUUID::validate(id_str))
+ {
+ executeSLURL("secondlife:///app/agent/" + id_str + "/requestfriend");
+ }
+}
+
+void LLUrlAction::removeFriend(std::string url)
+{
+ std::string id_str = getUserID(url);
+ if (LLUUID::validate(id_str))
+ {
+ executeSLURL("secondlife:///app/agent/" + id_str + "/removefriend");
+ }
+}
+
+void LLUrlAction::blockObject(std::string url)
+{
+ std::string object_id = getObjectId(url);
+ std::string object_name = getObjectName(url);
+ if (LLUUID::validate(object_id))
+ {
+ executeSLURL("secondlife:///app/agent/" + object_id + "/block/" + object_name);
+ }
+}
diff --git a/indra/llui/llurlaction.h b/indra/llui/llurlaction.h
index 0132dbaaf0..5f3626490c 100644..100755
--- a/indra/llui/llurlaction.h
+++ b/indra/llui/llurlaction.h
@@ -29,6 +29,7 @@
#define LL_LLURLACTION_H
#include <string>
+#include <boost/function.hpp>
///
/// The LLUrlAction class provides a number of static functions that
@@ -65,7 +66,7 @@ public:
static void showLocationOnMap(std::string url);
/// perform the appropriate action for left-clicking on a Url
- static void clickAction(std::string url);
+ static void clickAction(std::string url, bool trusted_content);
/// copy the label for a Url to the clipboard
static void copyLabelToClipboard(std::string url);
@@ -75,19 +76,30 @@ public:
/// if the Url specifies an SL command in the form like 'app/{cmd}/{id}/*', show its profile
static void showProfile(std::string url);
+ static std::string getUserID(std::string url);
+ static std::string getObjectName(std::string url);
+ static std::string getObjectId(std::string url);
+ static void sendIM(std::string url);
+ static void addFriend(std::string url);
+ static void removeFriend(std::string url);
+ static void blockObject(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));
+ typedef boost::function<void (const std::string&)> url_callback_t;
+ typedef boost::function<bool(const std::string& url, bool trusted_content)> execute_url_callback_t;
+
+ static void setOpenURLCallback(url_callback_t cb);
+ static void setOpenURLInternalCallback(url_callback_t cb);
+ static void setOpenURLExternalCallback(url_callback_t cb);
+ static void setExecuteSLURLCallback(execute_url_callback_t cb);
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);
+ static url_callback_t sOpenURLCallback;
+ static url_callback_t sOpenURLInternalCallback;
+ static url_callback_t sOpenURLExternalCallback;
+
+ static execute_url_callback_t sExecuteSLURLCallback;
};
#endif
diff --git a/indra/llui/llurlentry.cpp b/indra/llui/llurlentry.cpp
index 9db1feafd1..5f60d80858 100644..100755
--- a/indra/llui/llurlentry.cpp
+++ b/indra/llui/llurlentry.cpp
@@ -31,6 +31,7 @@
#include "lluri.h"
#include "llurlmatch.h"
#include "llurlregistry.h"
+#include "lluriparser.h"
#include "llavatarnamecache.h"
#include "llcachename.h"
@@ -45,7 +46,8 @@ std::string localize_slapp_label(const std::string& url, const std::string& full
LLUrlEntryBase::LLUrlEntryBase()
-{}
+{
+}
LLUrlEntryBase::~LLUrlEntryBase()
{
@@ -178,6 +180,36 @@ bool LLUrlEntryBase::isLinkDisabled() const
return globally_disabled;
}
+bool LLUrlEntryBase::isWikiLinkCorrect(std::string url)
+{
+ std::string label = getLabelFromWikiLink(url);
+ return (LLUrlRegistry::instance().hasUrl(label)) ? false : true;
+}
+
+std::string LLUrlEntryBase::urlToLabelWithGreyQuery(const std::string &url) const
+{
+ LLUriParser up(unescapeUrl(url));
+ up.normalize();
+
+ std::string label;
+ up.extractParts();
+ up.glueFirst(label);
+
+ return label;
+}
+
+std::string LLUrlEntryBase::urlToGreyQuery(const std::string &url) const
+{
+ LLUriParser up(unescapeUrl(url));
+
+ std::string query;
+ up.extractParts();
+ up.glueSecond(query);
+
+ return query;
+}
+
+
static std::string getStringAfterToken(const std::string str, const std::string token)
{
size_t pos = str.find(token);
@@ -194,6 +226,7 @@ static std::string getStringAfterToken(const std::string str, const std::string
// LLUrlEntryHTTP Describes generic http: and https: Urls
//
LLUrlEntryHTTP::LLUrlEntryHTTP()
+ : LLUrlEntryBase()
{
mPattern = boost::regex("https?://([-\\w\\.]+)+(:\\d+)?(:\\w+)?(@\\d+)?(@\\w+)?/?\\S*",
boost::regex::perl|boost::regex::icase);
@@ -203,6 +236,25 @@ LLUrlEntryHTTP::LLUrlEntryHTTP()
std::string LLUrlEntryHTTP::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
{
+ return urlToLabelWithGreyQuery(url);
+}
+
+std::string LLUrlEntryHTTP::getQuery(const std::string &url) const
+{
+ return urlToGreyQuery(url);
+}
+
+std::string LLUrlEntryHTTP::getUrl(const std::string &string) const
+{
+ if (string.find("://") == std::string::npos)
+ {
+ return "http://" + escapeUrl(string);
+ }
+ return escapeUrl(string);
+}
+
+std::string LLUrlEntryHTTP::getTooltip(const std::string &url) const
+{
return unescapeUrl(url);
}
@@ -238,6 +290,7 @@ std::string LLUrlEntryHTTPLabel::getUrl(const std::string &string) const
// LLUrlEntryHTTPNoProtocol Describes generic Urls like www.google.com
//
LLUrlEntryHTTPNoProtocol::LLUrlEntryHTTPNoProtocol()
+ : LLUrlEntryBase()
{
mPattern = boost::regex("("
"\\bwww\\.\\S+\\.\\S+" // i.e. www.FOO.BAR
@@ -251,7 +304,12 @@ LLUrlEntryHTTPNoProtocol::LLUrlEntryHTTPNoProtocol()
std::string LLUrlEntryHTTPNoProtocol::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
{
- return unescapeUrl(url);
+ return urlToLabelWithGreyQuery(url);
+}
+
+std::string LLUrlEntryHTTPNoProtocol::getQuery(const std::string &url) const
+{
+ return urlToGreyQuery(url);
}
std::string LLUrlEntryHTTPNoProtocol::getUrl(const std::string &string) const
@@ -263,6 +321,95 @@ std::string LLUrlEntryHTTPNoProtocol::getUrl(const std::string &string) const
return escapeUrl(string);
}
+std::string LLUrlEntryHTTPNoProtocol::getTooltip(const std::string &url) const
+{
+ return unescapeUrl(url);
+}
+
+LLUrlEntryInvalidSLURL::LLUrlEntryInvalidSLURL()
+ : LLUrlEntryBase()
+{
+ mPattern = boost::regex("(http://(maps.secondlife.com|slurl.com)/secondlife/|secondlife://(/app/(worldmap|teleport)/)?)[^ /]+(/-?[0-9]+){1,3}(/?(\\?title|\\?img|\\?msg)=\\S*)?/?",
+ boost::regex::perl|boost::regex::icase);
+ mMenuName = "menu_url_http.xml";
+ mTooltip = LLTrans::getString("TooltipHttpUrl");
+}
+
+std::string LLUrlEntryInvalidSLURL::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+{
+
+ return escapeUrl(url);
+}
+
+std::string LLUrlEntryInvalidSLURL::getUrl(const std::string &string) const
+{
+ return escapeUrl(string);
+}
+
+std::string LLUrlEntryInvalidSLURL::getTooltip(const std::string &url) const
+{
+ return unescapeUrl(url);
+}
+
+bool LLUrlEntryInvalidSLURL::isSLURLvalid(const std::string &url) const
+{
+ S32 actual_parts;
+
+ if(url.find(".com/secondlife/") != std::string::npos)
+ {
+ actual_parts = 5;
+ }
+ else if(url.find("/app/") != std::string::npos)
+ {
+ actual_parts = 6;
+ }
+ else
+ {
+ actual_parts = 3;
+ }
+
+ LLURI uri(url);
+ LLSD path_array = uri.pathArray();
+ S32 path_parts = path_array.size();
+ S32 x,y,z;
+
+ if (path_parts == actual_parts)
+ {
+ // handle slurl with (X,Y,Z) coordinates
+ LLStringUtil::convertToS32(path_array[path_parts-3],x);
+ LLStringUtil::convertToS32(path_array[path_parts-2],y);
+ LLStringUtil::convertToS32(path_array[path_parts-1],z);
+
+ if((x>= 0 && x<= 256) && (y>= 0 && y<= 256) && (z>= 0))
+ {
+ return TRUE;
+ }
+ }
+ else if (path_parts == (actual_parts-1))
+ {
+ // handle slurl with (X,Y) coordinates
+
+ LLStringUtil::convertToS32(path_array[path_parts-2],x);
+ LLStringUtil::convertToS32(path_array[path_parts-1],y);
+ ;
+ if((x>= 0 && x<= 256) && (y>= 0 && y<= 256))
+ {
+ return TRUE;
+ }
+ }
+ else if (path_parts == (actual_parts-2))
+ {
+ // handle slurl with (X) coordinate
+ LLStringUtil::convertToS32(path_array[path_parts-1],x);
+ if(x>= 0 && x<= 256)
+ {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
//
// LLUrlEntrySLURL Describes generic http: and https: Urls
//
@@ -284,6 +431,7 @@ std::string LLUrlEntrySLURL::getLabel(const std::string &url, const LLUrlLabelCa
// - 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();
@@ -336,11 +484,62 @@ std::string LLUrlEntrySLURL::getLocation(const std::string &url) const
}
//
+// LLUrlEntrySeconlifeURL Describes *secondlife.com/ and *lindenlab.com/ urls to substitute icon 'hand.png' before link
+//
+LLUrlEntrySecondlifeURL::LLUrlEntrySecondlifeURL()
+{
+ mPattern = boost::regex("(https?://)?([-\\w\\.]*\\.)?(secondlife|lindenlab)\\.com(:\\d{1,5})?\\/\\S*",
+ boost::regex::perl|boost::regex::icase);
+
+ mIcon = "Hand";
+ mMenuName = "menu_url_http.xml";
+ mTooltip = LLTrans::getString("TooltipHttpUrl");
+}
+
+/// Return the url from a string that matched the regex
+std::string LLUrlEntrySecondlifeURL::getUrl(const std::string &string) const
+{
+ if (string.find("://") == std::string::npos)
+ {
+ return "https://" + escapeUrl(string);
+ }
+ return escapeUrl(string);
+}
+
+std::string LLUrlEntrySecondlifeURL::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+{
+ return urlToLabelWithGreyQuery(url);
+}
+
+std::string LLUrlEntrySecondlifeURL::getQuery(const std::string &url) const
+{
+ return urlToGreyQuery(url);
+}
+
+std::string LLUrlEntrySecondlifeURL::getTooltip(const std::string &url) const
+{
+ return url;
+}
+
+//
+// LLUrlEntrySimpleSecondlifeURL Describes *secondlife.com and *lindenlab.com urls to substitute icon 'hand.png' before link
+//
+LLUrlEntrySimpleSecondlifeURL::LLUrlEntrySimpleSecondlifeURL()
+ {
+ mPattern = boost::regex("(https?://)?([-\\w\\.]*\\.)?(secondlife|lindenlab)\\.com(?!\\S)",
+ boost::regex::perl|boost::regex::icase);
+
+ mIcon = "Hand";
+ mMenuName = "menu_url_http.xml";
+}
+
+//
// LLUrlEntryAgent Describes a Second Life agent Url, e.g.,
// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about
// x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about
//
-LLUrlEntryAgent::LLUrlEntryAgent()
+LLUrlEntryAgent::LLUrlEntryAgent() :
+ mAvatarNameCacheConnection()
{
mPattern = boost::regex(APP_HEADER_REGEX "/agent/[\\da-f-]+/\\w+",
boost::regex::perl|boost::regex::icase);
@@ -371,7 +570,9 @@ void LLUrlEntryAgent::callObservers(const std::string &id,
void LLUrlEntryAgent::onAvatarNameCache(const LLUUID& id,
const LLAvatarName& av_name)
{
- std::string label = av_name.getCompleteName();
+ mAvatarNameCacheConnection.disconnect();
+
+ std::string label = av_name.getCompleteName();
// received the agent name from the server - tell our observers
callObservers(id.asString(), label, mIcon);
@@ -456,9 +657,11 @@ std::string LLUrlEntryAgent::getLabel(const std::string &url, const LLUrlLabelCa
}
else
{
- LLAvatarNameCache::get(agent_id,
- boost::bind(&LLUrlEntryAgent::onAvatarNameCache,
- this, _1, _2));
+ if (mAvatarNameCacheConnection.connected())
+ {
+ mAvatarNameCacheConnection.disconnect();
+ }
+ mAvatarNameCacheConnection = LLAvatarNameCache::get(agent_id, boost::bind(&LLUrlEntryAgent::onAvatarNameCache, this, _1, _2));
addObserver(agent_id_string, url, cb);
return LLTrans::getString("LoadingData");
}
@@ -499,6 +702,10 @@ std::string localize_slapp_label(const std::string& url, const std::string& full
{
return LLTrans::getString("SLappAgentRequestFriend") + " " + full_name;
}
+ if (LLStringUtil::endsWith(url, "/removefriend"))
+ {
+ return LLTrans::getString("SLappAgentRemoveFriend") + " " + full_name;
+ }
return full_name;
}
@@ -515,12 +722,15 @@ std::string LLUrlEntryAgent::getIcon(const std::string &url)
// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/(completename|displayname|username)
// x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/(completename|displayname|username)
//
-LLUrlEntryAgentName::LLUrlEntryAgentName()
+LLUrlEntryAgentName::LLUrlEntryAgentName() :
+ mAvatarNameCacheConnection()
{}
void LLUrlEntryAgentName::onAvatarNameCache(const LLUUID& id,
const LLAvatarName& av_name)
{
+ mAvatarNameCacheConnection.disconnect();
+
std::string label = getName(av_name);
// received the agent name from the server - tell our observers
callObservers(id.asString(), label, mIcon);
@@ -554,9 +764,11 @@ std::string LLUrlEntryAgentName::getLabel(const std::string &url, const LLUrlLab
}
else
{
- LLAvatarNameCache::get(agent_id,
- boost::bind(&LLUrlEntryAgentCompleteName::onAvatarNameCache,
- this, _1, _2));
+ if (mAvatarNameCacheConnection.connected())
+ {
+ mAvatarNameCacheConnection.disconnect();
+ }
+ mAvatarNameCacheConnection = LLAvatarNameCache::get(agent_id, boost::bind(&LLUrlEntryAgentName::onAvatarNameCache, this, _1, _2));
addObserver(agent_id_string, url, cb);
return LLTrans::getString("LoadingData");
}
@@ -597,7 +809,7 @@ LLUrlEntryAgentDisplayName::LLUrlEntryAgentDisplayName()
std::string LLUrlEntryAgentDisplayName::getName(const LLAvatarName& avatar_name)
{
- return avatar_name.mDisplayName;
+ return avatar_name.getDisplayName();
}
//
@@ -613,7 +825,7 @@ LLUrlEntryAgentUserName::LLUrlEntryAgentUserName()
std::string LLUrlEntryAgentUserName::getName(const LLAvatarName& avatar_name)
{
- return avatar_name.mUsername.empty() ? avatar_name.getLegacyName() : avatar_name.mUsername;
+ return avatar_name.getAccountName();
}
//
@@ -775,7 +987,7 @@ std::string LLUrlEntryParcel::getLabel(const std::string &url, const LLUrlLabelC
if (path_parts < 3) // no parcel id
{
- llwarns << "Failed to parse url [" << url << "]" << llendl;
+ LL_WARNS() << "Failed to parse url [" << url << "]" << LL_ENDL;
return url;
}
@@ -821,9 +1033,9 @@ void LLUrlEntryParcel::processParcelInfo(const LLParcelData& parcel_data)
// If parcel name is empty use Sim_name (x, y, z) for parcel label.
else if (!parcel_data.sim_name.empty())
{
- S32 region_x = llround(parcel_data.global_x) % REGION_WIDTH_UNITS;
- S32 region_y = llround(parcel_data.global_y) % REGION_WIDTH_UNITS;
- S32 region_z = llround(parcel_data.global_z);
+ S32 region_x = ll_round(parcel_data.global_x) % REGION_WIDTH_UNITS;
+ S32 region_y = ll_round(parcel_data.global_y) % REGION_WIDTH_UNITS;
+ S32 region_z = ll_round(parcel_data.global_z);
label = llformat("%s (%d, %d, %d)",
parcel_data.sim_name.c_str(), region_x, region_y, region_z);
@@ -915,7 +1127,7 @@ std::string LLUrlEntryRegion::getLabel(const std::string &url, const LLUrlLabelC
if (path_parts < 3) // no region name
{
- llwarns << "Failed to parse url [" << url << "]" << llendl;
+ LL_WARNS() << "Failed to parse url [" << url << "]" << LL_ENDL;
return url;
}
@@ -1053,7 +1265,8 @@ LLUrlEntrySLLabel::LLUrlEntrySLLabel()
std::string LLUrlEntrySLLabel::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
{
- return getLabelFromWikiLink(url);
+ std::string label = getLabelFromWikiLink(url);
+ return (!LLUrlRegistry::instance().hasUrl(label)) ? label : getUrl(url);
}
std::string LLUrlEntrySLLabel::getUrl(const std::string &string) const
@@ -1134,7 +1347,7 @@ std::string LLUrlEntryWorldMap::getLocation(const std::string &url) const
//
LLUrlEntryNoLink::LLUrlEntryNoLink()
{
- mPattern = boost::regex("<nolink>.*</nolink>",
+ mPattern = boost::regex("<nolink>.*?</nolink>",
boost::regex::perl|boost::regex::icase);
}
diff --git a/indra/llui/llurlentry.h b/indra/llui/llurlentry.h
index 5f82721c0f..60a494974f 100644..100755
--- a/indra/llui/llurlentry.h
+++ b/indra/llui/llurlentry.h
@@ -32,6 +32,7 @@
#include "lluicolor.h"
#include "llstyle.h"
+#include "llavatarname.h"
#include "llhost.h" // for resolving parcel name by parcel id
#include <boost/signals2.hpp>
@@ -77,6 +78,9 @@ public:
/// Given a matched Url, return a label for the Url
virtual std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) { return url; }
+ /// Return port, query and fragment parts for the Url
+ virtual std::string getQuery(const std::string &url) const { return ""; }
+
/// Return an icon that can be displayed next to Urls of this type
virtual std::string getIcon(const std::string &url);
@@ -95,10 +99,16 @@ public:
/// Should this link text be underlined only when mouse is hovered over it?
virtual bool underlineOnHoverOnly(const std::string &string) const { return false; }
+ virtual bool isTrusted() const { return false; }
+
virtual LLUUID getID(const std::string &string) const { return LLUUID::null; }
bool isLinkDisabled() const;
+ bool isWikiLinkCorrect(std::string url);
+
+ virtual bool isSLURLvalid(const std::string &url) const { return TRUE; };
+
protected:
std::string getIDStringFromUrl(const std::string &url) const;
std::string escapeUrl(const std::string &url) const;
@@ -106,6 +116,8 @@ protected:
std::string getLabelFromWikiLink(const std::string &url) const;
std::string getUrlFromWikiLink(const std::string &string) const;
void addObserver(const std::string &id, const std::string &url, const LLUrlLabelCallback &cb);
+ std::string urlToLabelWithGreyQuery(const std::string &url) const;
+ std::string urlToGreyQuery(const std::string &url) const;
virtual void callObservers(const std::string &id, const std::string &label, const std::string& icon);
typedef struct {
@@ -128,6 +140,9 @@ class LLUrlEntryHTTP : public LLUrlEntryBase
public:
LLUrlEntryHTTP();
/*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
+ /*virtual*/ std::string getQuery(const std::string &url) const;
+ /*virtual*/ std::string getUrl(const std::string &string) const;
+ /*virtual*/ std::string getTooltip(const std::string &url) const;
};
///
@@ -150,7 +165,20 @@ class LLUrlEntryHTTPNoProtocol : public LLUrlEntryBase
public:
LLUrlEntryHTTPNoProtocol();
/*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
+ /*virtual*/ std::string getQuery(const std::string &url) const;
+ /*virtual*/ std::string getUrl(const std::string &string) const;
+ /*virtual*/ std::string getTooltip(const std::string &url) const;
+};
+
+class LLUrlEntryInvalidSLURL : public LLUrlEntryBase
+{
+public:
+ LLUrlEntryInvalidSLURL();
+ /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
/*virtual*/ std::string getUrl(const std::string &string) const;
+ /*virtual*/ std::string getTooltip(const std::string &url) const;
+
+ bool isSLURLvalid(const std::string &url) const;
};
///
@@ -165,12 +193,42 @@ public:
};
///
+/// LLUrlEntrySeconlifeURLs Describes *secondlife.com and *lindenlab.com Urls
+///
+class LLUrlEntrySecondlifeURL : public LLUrlEntryBase
+{
+public:
+ LLUrlEntrySecondlifeURL();
+ /*virtual*/ bool isTrusted() const { return true; }
+ /*virtual*/ std::string getUrl(const std::string &string) const;
+ /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
+ /*virtual*/ std::string getQuery(const std::string &url) const;
+ /*virtual*/ std::string getTooltip(const std::string &url) const;
+};
+
+///
+/// LLUrlEntrySeconlifeURLs Describes *secondlife.com and *lindenlab.com Urls
+///
+class LLUrlEntrySimpleSecondlifeURL : public LLUrlEntrySecondlifeURL
+{
+public:
+ LLUrlEntrySimpleSecondlifeURL();
+};
+
+///
/// LLUrlEntryAgent Describes a Second Life agent Url, e.g.,
/// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about
class LLUrlEntryAgent : public LLUrlEntryBase
{
public:
LLUrlEntryAgent();
+ ~LLUrlEntryAgent()
+ {
+ if (mAvatarNameCacheConnection.connected())
+ {
+ mAvatarNameCacheConnection.disconnect();
+ }
+ }
/*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
/*virtual*/ std::string getIcon(const std::string &url);
/*virtual*/ std::string getTooltip(const std::string &string) const;
@@ -181,6 +239,7 @@ protected:
/*virtual*/ void callObservers(const std::string &id, const std::string &label, const std::string& icon);
private:
void onAvatarNameCache(const LLUUID& id, const LLAvatarName& av_name);
+ boost::signals2::connection mAvatarNameCacheConnection;
};
///
@@ -192,6 +251,13 @@ class LLUrlEntryAgentName : public LLUrlEntryBase, public boost::signals2::track
{
public:
LLUrlEntryAgentName();
+ ~LLUrlEntryAgentName()
+ {
+ if (mAvatarNameCacheConnection.connected())
+ {
+ mAvatarNameCacheConnection.disconnect();
+ }
+ }
/*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
/*virtual*/ LLStyle::Params getStyle() const;
protected:
@@ -199,6 +265,7 @@ protected:
virtual std::string getName(const LLAvatarName& avatar_name) = 0;
private:
void onAvatarNameCache(const LLUUID& id, const LLAvatarName& av_name);
+ boost::signals2::connection mAvatarNameCacheConnection;
};
diff --git a/indra/llui/llurlmatch.cpp b/indra/llui/llurlmatch.cpp
index c1f1382a9f..2f2ac969e1 100644..100755
--- a/indra/llui/llurlmatch.cpp
+++ b/indra/llui/llurlmatch.cpp
@@ -37,20 +37,22 @@ LLUrlMatch::LLUrlMatch() :
mIcon(""),
mMenuName(""),
mLocation(""),
- mUnderlineOnHoverOnly(false)
+ mUnderlineOnHoverOnly(false),
+ mTrusted(false)
{
}
-void LLUrlMatch::setValues(U32 start, U32 end, const std::string &url,
- const std::string &label, const std::string &tooltip,
+void LLUrlMatch::setValues(U32 start, U32 end, const std::string &url, const std::string &label,
+ const std::string& query, const std::string &tooltip,
const std::string &icon, const LLStyle::Params& style,
const std::string &menu, const std::string &location,
- const LLUUID& id, bool underline_on_hover_only)
+ const LLUUID& id, bool underline_on_hover_only, bool trusted)
{
mStart = start;
mEnd = end;
mUrl = url;
mLabel = label;
+ mQuery = query;
mTooltip = tooltip;
mIcon = icon;
mStyle = style;
@@ -59,4 +61,5 @@ void LLUrlMatch::setValues(U32 start, U32 end, const std::string &url,
mLocation = location;
mID = id;
mUnderlineOnHoverOnly = underline_on_hover_only;
+ mTrusted = trusted;
}
diff --git a/indra/llui/llurlmatch.h b/indra/llui/llurlmatch.h
index 2818f45207..ff699902ca 100644..100755
--- a/indra/llui/llurlmatch.h
+++ b/indra/llui/llurlmatch.h
@@ -62,6 +62,9 @@ public:
/// return a label that can be used for the display of this Url
std::string getLabel() const { return mLabel; }
+ /// return a right part of url which should be drawn in grey
+ std::string getQuery() const { return mQuery; }
+
/// return a message that could be displayed in a tooltip or status bar
std::string getTooltip() const { return mTooltip; }
@@ -80,12 +83,15 @@ public:
/// Should this link text be underlined only when mouse is hovered over it?
bool underlineOnHoverOnly() const { return mUnderlineOnHoverOnly; }
+ /// Return true if Url is trusted.
+ bool isTrusted() const { return mTrusted; }
+
/// 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 std::string& query, const std::string &tooltip, const std::string &icon,
const LLStyle::Params& style, const std::string &menu,
const std::string &location, const LLUUID& id,
- bool underline_on_hover_only = false );
+ bool underline_on_hover_only = false, bool trusted = false);
const LLUUID& getID() const { return mID; }
private:
@@ -93,6 +99,7 @@ private:
U32 mEnd;
std::string mUrl;
std::string mLabel;
+ std::string mQuery;
std::string mTooltip;
std::string mIcon;
std::string mMenuName;
@@ -100,6 +107,7 @@ private:
LLUUID mID;
LLStyle::Params mStyle;
bool mUnderlineOnHoverOnly;
+ bool mTrusted;
};
#endif
diff --git a/indra/llui/llurlregistry.cpp b/indra/llui/llurlregistry.cpp
index 523ee5d78c..1143574968 100644..100755
--- a/indra/llui/llurlregistry.cpp
+++ b/indra/llui/llurlregistry.cpp
@@ -27,6 +27,7 @@
#include "linden_common.h"
#include "llurlregistry.h"
+#include "lluriparser.h"
#include <boost/regex.hpp>
@@ -41,10 +42,19 @@ LLUrlRegistry::LLUrlRegistry()
// Urls are matched in the order that they were registered
registerUrl(new LLUrlEntryNoLink());
- registerUrl(new LLUrlEntryIcon());
+ mUrlEntryIcon = new LLUrlEntryIcon();
+ registerUrl(mUrlEntryIcon);
+ mLLUrlEntryInvalidSLURL = new LLUrlEntryInvalidSLURL();
+ registerUrl(mLLUrlEntryInvalidSLURL);
registerUrl(new LLUrlEntrySLURL());
+
+ // decorated links for host names like: secondlife.com and lindenlab.com
+ registerUrl(new LLUrlEntrySecondlifeURL());
+ registerUrl(new LLUrlEntrySimpleSecondlifeURL());
+
registerUrl(new LLUrlEntryHTTP());
- registerUrl(new LLUrlEntryHTTPLabel());
+ mUrlEntryHTTPLabel = new LLUrlEntryHTTPLabel();
+ registerUrl(mUrlEntryHTTPLabel);
registerUrl(new LLUrlEntryAgentCompleteName());
registerUrl(new LLUrlEntryAgentDisplayName());
registerUrl(new LLUrlEntryAgentUserName());
@@ -63,7 +73,8 @@ LLUrlRegistry::LLUrlRegistry()
//LLUrlEntrySL and LLUrlEntrySLLabel have more common pattern,
//so it should be registered in the end of list
registerUrl(new LLUrlEntrySL());
- registerUrl(new LLUrlEntrySLLabel());
+ mUrlEntrySLLabel = new LLUrlEntrySLLabel();
+ registerUrl(mUrlEntrySLLabel);
// most common pattern is a URL without any protocol,
// e.g., "secondlife.com"
registerUrl(new LLUrlEntryHTTPNoProtocol());
@@ -127,6 +138,11 @@ static bool matchRegex(const char *text, boost::regex regex, U32 &start, U32 &en
end--;
}
+ else if (text[end] == ']' && std::string(text+start, end-start).find('[') == std::string::npos)
+ {
+ end--;
+ }
+
return true;
}
@@ -145,7 +161,7 @@ static bool stringHasUrl(const std::string &text)
text.find("<icon") != std::string::npos);
}
-bool LLUrlRegistry::findUrl(const std::string &text, LLUrlMatch &match, const LLUrlLabelCallback &cb)
+bool LLUrlRegistry::findUrl(const std::string &text, LLUrlMatch &match, const LLUrlLabelCallback &cb, bool is_content_trusted)
{
// avoid costly regexes if there is clearly no URL in the text
if (! stringHasUrl(text))
@@ -160,6 +176,12 @@ bool LLUrlRegistry::findUrl(const std::string &text, LLUrlMatch &match, const LL
std::vector<LLUrlEntryBase *>::iterator it;
for (it = mUrlEntry.begin(); it != mUrlEntry.end(); ++it)
{
+ //Skip for url entry icon if content is not trusted
+ if(!is_content_trusted && (mUrlEntryIcon == *it))
+ {
+ continue;
+ }
+
LLUrlEntryBase *url_entry = *it;
U32 start = 0, end = 0;
@@ -168,6 +190,23 @@ bool LLUrlRegistry::findUrl(const std::string &text, LLUrlMatch &match, const LL
// does this match occur in the string before any other match
if (start < match_start || match_entry == NULL)
{
+
+ if (mLLUrlEntryInvalidSLURL == *it)
+ {
+ if(url_entry && url_entry->isSLURLvalid(text.substr(start, end - start + 1)))
+ {
+ continue;
+ }
+ }
+
+ if((mUrlEntryHTTPLabel == *it) || (mUrlEntrySLLabel == *it))
+ {
+ if(url_entry && !url_entry->isWikiLinkCorrect(text.substr(start, end - start + 1)))
+ {
+ continue;
+ }
+ }
+
match_start = start;
match_end = end;
match_entry = url_entry;
@@ -180,16 +219,26 @@ bool LLUrlRegistry::findUrl(const std::string &text, LLUrlMatch &match, const LL
{
// fill in the LLUrlMatch object and return it
std::string url = text.substr(match_start, match_end - match_start + 1);
+
+ if (match_entry == mUrlEntryTrusted)
+ {
+ LLUriParser up(url);
+ up.normalize();
+ url = up.normalizedUri();
+ }
+
match.setValues(match_start, match_end,
match_entry->getUrl(url),
match_entry->getLabel(url, cb),
+ match_entry->getQuery(url),
match_entry->getTooltip(url),
match_entry->getIcon(url),
match_entry->getStyle(),
match_entry->getMenuName(),
match_entry->getLocation(url),
match_entry->getID(url),
- match_entry->underlineOnHoverOnly(url));
+ match_entry->underlineOnHoverOnly(url),
+ match_entry->isTrusted());
return true;
}
@@ -209,7 +258,7 @@ bool LLUrlRegistry::findUrl(const LLWString &text, LLUrlMatch &match, const LLUr
// 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);
+ size_t start = text.find(wurl);
if (start == std::string::npos)
{
return false;
@@ -218,6 +267,7 @@ bool LLUrlRegistry::findUrl(const LLWString &text, LLUrlMatch &match, const LLUr
match.setValues(start, end, match.getUrl(),
match.getLabel(),
+ match.getQuery(),
match.getTooltip(),
match.getIcon(),
match.getStyle(),
diff --git a/indra/llui/llurlregistry.h b/indra/llui/llurlregistry.h
index da16171a97..5ce4048d5d 100644..100755
--- a/indra/llui/llurlregistry.h
+++ b/indra/llui/llurlregistry.h
@@ -73,7 +73,8 @@ public:
/// 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);
+ const LLUrlLabelCallback &cb = &LLUrlRegistryNullCallback,
+ bool is_content_trusted = false);
/// a slightly less efficient version of findUrl for wide strings
bool findUrl(const LLWString &text, LLUrlMatch &match,
@@ -92,6 +93,11 @@ private:
friend class LLSingleton<LLUrlRegistry>;
std::vector<LLUrlEntryBase *> mUrlEntry;
+ LLUrlEntryBase* mUrlEntryTrusted;
+ LLUrlEntryBase* mUrlEntryIcon;
+ LLUrlEntryBase* mLLUrlEntryInvalidSLURL;
+ LLUrlEntryBase* mUrlEntryHTTPLabel;
+ LLUrlEntryBase* mUrlEntrySLLabel;
};
#endif
diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp
index 267640a226..a8beb9cfc9 100644..100755
--- a/indra/llui/llview.cpp
+++ b/indra/llui/llview.cpp
@@ -30,8 +30,10 @@
#define LLVIEW_CPP
#include "llview.h"
-#include <cassert>
+#include <sstream>
#include <boost/tokenizer.hpp>
+#include <boost/foreach.hpp>
+#include <boost/bind.hpp>
#include "llrender.h"
#include "llevent.h"
@@ -44,13 +46,18 @@
#include "v3color.h"
#include "lluictrlfactory.h"
#include "lltooltip.h"
-
+#include "llsdutil.h"
+#include "llsdserialize.h"
+#include "llviewereventrecorder.h"
+#include "llkeyboard.h"
// for ui edit hack
#include "llbutton.h"
#include "lllineeditor.h"
#include "lltexteditor.h"
#include "lltextbox.h"
+static const S32 LINE_HEIGHT = 15;
+
S32 LLView::sDepth = 0;
bool LLView::sDebugRects = false;
bool LLView::sDebugRectsShowNames = true;
@@ -66,6 +73,8 @@ S32 LLView::sLastLeftXML = S32_MIN;
S32 LLView::sLastBottomXML = S32_MIN;
std::vector<LLViewDrawContext*> LLViewDrawContext::sDrawContextStack;
+LLView::DrilldownFunc LLView::sDrilldown =
+ boost::bind(&LLView::pointInView, _1, _2, _3, HIT_TEST_USE_BOUNDING_RECT);
//#if LL_DEBUG
BOOL LLView::sIsDrawing = FALSE;
@@ -77,6 +86,16 @@ template class LLView* LLView::getChild<class LLView>(
static LLDefaultChildRegistry::Register<LLView> r("view");
+namespace LLInitParam
+{
+ void TypeValues<LLView::EOrientation>::declareValues()
+ {
+ declare("horizontal", LLView::HORIZONTAL);
+ declare("vertical", LLView::VERTICAL);
+ }
+}
+
+
LLView::Follows::Follows()
: string(""),
flags("flags", FOLLOWS_LEFT | FOLLOWS_TOP)
@@ -114,16 +133,17 @@ LLView::Params::Params()
}
LLView::LLView(const LLView::Params& p)
-: mName(p.name),
+: LLTrace::MemTrackable<LLView>("LLView"),
+ mVisible(p.visible),
+ mInDraw(false),
+ mName(p.name),
mParentView(NULL),
mReshapeFlags(FOLLOWS_NONE),
mFromXUI(p.from_xui),
mIsFocusRoot(p.focus_root),
mLastVisible(FALSE),
- mNextInsertionOrdinal(0),
mHoverCursor(getCursorFromString(p.hover_cursor)),
mEnabled(p.enabled),
- mVisible(p.visible),
mMouseOpaque(p.mouse_opaque),
mSoundFlags(p.sound_flags),
mUseBoundingRect(p.use_bounding_rect),
@@ -140,10 +160,10 @@ LLView::LLView(const LLView::Params& p)
LLView::~LLView()
{
dirtyRect();
- //llinfos << "Deleting view " << mName << ":" << (void*) this << llendl;
+ //LL_INFOS() << "Deleting view " << mName << ":" << (void*) this << LL_ENDL;
if (LLView::sIsDrawing)
{
- lldebugs << "Deleting view " << mName << " during UI draw() phase" << llendl;
+ LL_DEBUGS() << "Deleting view " << mName << " during UI draw() phase" << LL_ENDL;
}
// llassert(LLView::sIsDrawing == FALSE);
@@ -151,7 +171,7 @@ LLView::~LLView()
if( hasMouseCapture() )
{
- //llwarns << "View holding mouse capture deleted: " << getName() << ". Mouse capture removed." << llendl;
+ //LL_WARNS() << "View holding mouse capture deleted: " << getName() << ". Mouse capture removed." << LL_ENDL;
gFocusMgr.removeMouseCaptureWithoutCallback( this );
}
@@ -219,9 +239,11 @@ BOOL LLView::getUseBoundingRect() const
}
// virtual
-std::string LLView::getName() const
+const std::string& LLView::getName() const
{
- return mName.empty() ? std::string("(no name)") : mName;
+ static std::string no_name("(no name)");
+
+ return mName.empty() ? no_name : mName;
}
void LLView::sendChildToFront(LLView* child)
@@ -254,22 +276,6 @@ void LLView::sendChildToBack(LLView* child)
}
}
-void LLView::moveChildToFrontOfTabGroup(LLUICtrl* child)
-{
- if(mCtrlOrder.find(child) != mCtrlOrder.end())
- {
- mCtrlOrder[child].second = -1 * mNextInsertionOrdinal++;
- }
-}
-
-void LLView::moveChildToBackOfTabGroup(LLUICtrl* child)
-{
- if(mCtrlOrder.find(child) != mCtrlOrder.end())
- {
- mCtrlOrder[child].second = mNextInsertionOrdinal++;
- }
-}
-
// virtual
bool LLView::addChild(LLView* child, S32 tab_group)
{
@@ -277,9 +283,10 @@ bool LLView::addChild(LLView* child, S32 tab_group)
{
return false;
}
- if (mParentView == child)
+
+ if (this == child)
{
- llerrs << "Adding view " << child->getName() << " as child of itself" << llendl;
+ LL_ERRS() << "Adding view " << child->getName() << " as child of itself" << LL_ENDL;
}
// remove from current parent
@@ -291,14 +298,10 @@ bool LLView::addChild(LLView* child, S32 tab_group)
// add to front of child list, as normal
mChildList.push_front(child);
- // add to ctrl list if is LLUICtrl
- if (child->isCtrl())
+ // add to tab order list
+ if (tab_group != 0)
{
- LLUICtrl* ctrl = static_cast<LLUICtrl*>(child);
- mCtrlOrder.insert(tab_order_pair_t(ctrl,
- tab_order_t(tab_group, mNextInsertionOrdinal)));
-
- mNextInsertionOrdinal++;
+ mTabOrder.insert(tab_order_pair_t(child, tab_group));
}
child->mParentView = this;
@@ -325,73 +328,23 @@ void LLView::removeChild(LLView* child)
//llassert_always(sDepth == 0); // Avoid re-ordering while drawing; it can cause subtle iterator bugs
if (child->mParentView == this)
{
+ // if we are removing an item we are currently iterating over, that would be bad
+ llassert(child->mInDraw == false);
mChildList.remove( child );
child->mParentView = NULL;
- if (child->isCtrl())
+ child_tab_order_t::iterator found = mTabOrder.find(child);
+ if(found != mTabOrder.end())
{
- child_tab_order_t::iterator found = mCtrlOrder.find(static_cast<LLUICtrl*>(child));
- if(found != mCtrlOrder.end())
- {
- mCtrlOrder.erase(found);
- }
+ mTabOrder.erase(found);
}
}
else
{
- llerrs << "LLView::removeChild called with non-child" << llendl;
+ LL_WARNS() << "\"" << child->getName() << "\" is not a child of " << getName() << LL_ENDL;
}
updateBoundingRect();
}
-LLView::ctrl_list_t LLView::getCtrlList() const
-{
- ctrl_list_t controls;
- for(child_list_const_iter_t iter = mChildList.begin();
- iter != mChildList.end();
- iter++)
- {
- if((*iter)->isCtrl())
- {
- controls.push_back(static_cast<LLUICtrl*>(*iter));
- }
- }
- return controls;
-}
-
-LLView::ctrl_list_t LLView::getCtrlListSorted() const
-{
- ctrl_list_t controls = getCtrlList();
- std::sort(controls.begin(), controls.end(), LLCompareByTabOrder(mCtrlOrder));
- return controls;
-}
-
-
-// This method compares two LLViews by the tab order specified in the comparator object. The
-// code for this is a little convoluted because each argument can have four states:
-// 1) not a control, 2) a control but not in the tab order, 3) a control in the tab order, 4) null
-bool LLCompareByTabOrder::operator() (const LLView* const a, const LLView* const b) const
-{
- S32 a_score = 0, b_score = 0;
- if(a) a_score--;
- if(b) b_score--;
- if(a && a->isCtrl()) a_score--;
- if(b && b->isCtrl()) b_score--;
- if(a_score == -2 && b_score == -2)
- {
- const LLUICtrl * const a_ctrl = static_cast<const LLUICtrl*>(a);
- const LLUICtrl * const b_ctrl = static_cast<const LLUICtrl*>(b);
- LLView::child_tab_order_const_iter_t a_found = mTabOrder.find(a_ctrl), b_found = mTabOrder.find(b_ctrl);
- if(a_found != mTabOrder.end()) a_score--;
- if(b_found != mTabOrder.end()) b_score--;
- if(a_score == -3 && b_score == -3)
- {
- // whew! Once we're in here, they're both in the tab order, and we can compare based on that
- return compareTabOrders(a_found->second, b_found->second);
- }
- }
- return (a_score == b_score) ? a < b : a_score < b_score;
-}
-
BOOL LLView::isInVisibleChain() const
{
BOOL visible = TRUE;
@@ -428,6 +381,36 @@ BOOL LLView::isInEnabledChain() const
return enabled;
}
+static void buildPathname(std::ostream& out, const LLView* view)
+{
+ if (! (view && view->getParent()))
+ {
+ return; // Don't include root in the path.
+ }
+
+ buildPathname(out, view->getParent());
+
+ // Build pathname into ostream on the way back from recursion.
+ out << '/' << view->getName();
+}
+
+std::string LLView::getPathname() const
+{
+ std::ostringstream out;
+ buildPathname(out, this);
+ return out.str();
+}
+
+//static
+std::string LLView::getPathname(const LLView* view)
+{
+ if (! view)
+ {
+ return "NULL";
+ }
+ return view->getPathname();
+}
+
// virtual
BOOL LLView::canFocusChildren() const
{
@@ -485,9 +468,9 @@ BOOL LLView::focusPrevRoot()
// static
BOOL LLView::focusNext(LLView::child_list_t & result)
{
- LLView::child_list_iter_t focused = result.end();
- for(LLView::child_list_iter_t iter = result.begin();
- iter != result.end();
+ LLView::child_list_reverse_iter_t focused = result.rend();
+ for(LLView::child_list_reverse_iter_t iter = result.rbegin();
+ iter != result.rend();
++iter)
{
if(gFocusMgr.childHasKeyboardFocus(*iter))
@@ -496,14 +479,14 @@ BOOL LLView::focusNext(LLView::child_list_t & result)
break;
}
}
- LLView::child_list_iter_t next = focused;
- next = (next == result.end()) ? result.begin() : ++next;
+ LLView::child_list_reverse_iter_t next = focused;
+ next = (next == result.rend()) ? result.rbegin() : ++next;
while(next != focused)
{
// wrap around to beginning if necessary
- if(next == result.end())
+ if(next == result.rend())
{
- next = result.begin();
+ next = result.rbegin();
}
if((*next)->isCtrl())
{
@@ -521,9 +504,9 @@ BOOL LLView::focusNext(LLView::child_list_t & result)
// static
BOOL LLView::focusPrev(LLView::child_list_t & result)
{
- LLView::child_list_reverse_iter_t focused = result.rend();
- for(LLView::child_list_reverse_iter_t iter = result.rbegin();
- iter != result.rend();
+ LLView::child_list_iter_t focused = result.end();
+ for(LLView::child_list_iter_t iter = result.begin();
+ iter != result.end();
++iter)
{
if(gFocusMgr.childHasKeyboardFocus(*iter))
@@ -532,14 +515,14 @@ BOOL LLView::focusPrev(LLView::child_list_t & result)
break;
}
}
- LLView::child_list_reverse_iter_t next = focused;
- next = (next == result.rend()) ? result.rbegin() : ++next;
+ LLView::child_list_iter_t next = focused;
+ next = (next == result.end()) ? result.begin() : ++next;
while(next != focused)
{
// wrap around to beginning if necessary
- if(next == result.rend())
+ if(next == result.end())
{
- next = result.rbegin();
+ next = result.begin();
}
if((*next)->isCtrl())
{
@@ -563,7 +546,7 @@ BOOL LLView::focusPrev(LLView::child_list_t & result)
void LLView::deleteAllChildren()
{
// clear out the control ordering
- mCtrlOrder.clear();
+ mTabOrder.clear();
while (!mChildList.empty())
{
@@ -574,9 +557,8 @@ void LLView::deleteAllChildren()
void LLView::setAllChildrenEnabled(BOOL b)
{
- for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
+ BOOST_FOREACH(LLView* viewp, mChildList)
{
- LLView* viewp = *child_it;
viewp->setEnabled(b);
}
}
@@ -593,22 +575,42 @@ void LLView::setVisible(BOOL visible)
{
// tell all children of this view that the visibility may have changed
dirtyRect();
- handleVisibilityChange( visible );
+ onVisibilityChange( visible );
}
updateBoundingRect();
}
}
// virtual
-void LLView::handleVisibilityChange ( BOOL new_visibility )
+void LLView::onVisibilityChange ( BOOL new_visibility )
{
- for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
+ BOOL old_visibility;
+ BOOL log_visibility_change = LLViewerEventRecorder::instance().getLoggingStatus();
+ BOOST_FOREACH(LLView* viewp, mChildList)
{
- LLView* viewp = *child_it;
// only views that are themselves visible will have their overall visibility affected by their ancestors
- if (viewp->getVisible())
+ old_visibility=viewp->getVisible();
+
+ if(log_visibility_change)
{
- viewp->handleVisibilityChange ( new_visibility );
+ if (old_visibility!=new_visibility)
+ {
+ LLViewerEventRecorder::instance().logVisibilityChange( viewp->getPathname(), viewp->getName(), new_visibility,"widget");
+ }
+ }
+
+ if (old_visibility)
+ {
+ viewp->onVisibilityChange ( new_visibility );
+ }
+
+ if(log_visibility_change)
+ {
+ // Consider changing returns to confirm success and know which widget grabbed it
+ // For now assume success and log at highest xui possible
+ // NOTE we log actual state - which may differ if it somehow failed to set visibility
+ LL_DEBUGS() << "LLView::handleVisibilityChange - now: " << getVisible() << " xui: " << viewp->getPathname() << " name: " << viewp->getName() << LL_ENDL;
+
}
}
}
@@ -638,64 +640,196 @@ BOOL LLView::handleHover(S32 x, S32 y, MASK mask)
void LLView::onMouseEnter(S32 x, S32 y, MASK mask)
{
- //llinfos << "Mouse entered " << getName() << llendl;
+ //LL_INFOS() << "Mouse entered " << getName() << LL_ENDL;
}
void LLView::onMouseLeave(S32 x, S32 y, MASK mask)
{
- //llinfos << "Mouse left " << getName() << llendl;
+ //LL_INFOS() << "Mouse left " << getName() << LL_ENDL;
}
+bool LLView::visibleAndContains(S32 local_x, S32 local_y)
+{
+ return sDrilldown(this, local_x, local_y)
+ && getVisible();
+}
+
+bool LLView::visibleEnabledAndContains(S32 local_x, S32 local_y)
+{
+ return visibleAndContains(local_x, local_y)
+ && getEnabled();
+}
+
+// This is NOT event recording related
+void LLView::logMouseEvent()
+{
+ if (sDebugMouseHandling)
+ {
+ sMouseHandlerMessage = std::string("/") + mName + sMouseHandlerMessage;
+ }
+}
+
+template <typename METHOD, typename CHARTYPE>
+LLView* LLView::childrenHandleCharEvent(const std::string& desc, const METHOD& method,
+ CHARTYPE c, MASK mask)
+{
+ if ( getVisible() && getEnabled() )
+ {
+ BOOST_FOREACH(LLView* viewp, mChildList)
+ {
+ if ((viewp->*method)(c, mask, TRUE))
+ {
+ if (LLView::sDebugKeys)
+ {
+ LL_INFOS() << desc << " handled by " << viewp->getName() << LL_ENDL;
+ }
+ return viewp;
+ }
+ }
+ }
+ return NULL;
+}
+
+// XDATA might be MASK, or S32 clicks
+template <typename METHOD, typename XDATA>
+LLView* LLView::childrenHandleMouseEvent(const METHOD& method, S32 x, S32 y, XDATA extra, bool allow_mouse_block)
+{
+ BOOST_FOREACH(LLView* viewp, mChildList)
+ {
+ S32 local_x = x - viewp->getRect().mLeft;
+ S32 local_y = y - viewp->getRect().mBottom;
+
+ if (!viewp->visibleEnabledAndContains(local_x, local_y))
+ {
+ continue;
+ }
+
+ if ((viewp->*method)( local_x, local_y, extra )
+ || (allow_mouse_block && viewp->blockMouseEvent( local_x, local_y )))
+ {
+ LL_DEBUGS() << "LLView::childrenHandleMouseEvent calling updatemouseeventinfo - local_x|global x "<< local_x << " " << x << "local/global y " << local_y << " " << y << LL_ENDL;
+ LL_DEBUGS() << "LLView::childrenHandleMouseEvent getPathname for viewp result: " << viewp->getPathname() << "for this view: " << getPathname() << LL_ENDL;
+
+ LLViewerEventRecorder::instance().updateMouseEventInfo(x,y,-55,-55,getPathname());
+
+ // This is NOT event recording related
+ viewp->logMouseEvent();
+
+ return viewp;
+ }
+ }
+ return NULL;
+}
LLView* LLView::childrenHandleToolTip(S32 x, S32 y, MASK mask)
{
- LLView* handled_view = NULL;
- for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
+ BOOST_FOREACH(LLView* viewp, mChildList)
{
- 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())
+ // Differs from childrenHandleMouseEvent() in that we want to offer
+ // tooltips even for disabled widgets.
+ if(!viewp->visibleAndContains(local_x, local_y))
{
continue;
}
- if (viewp->handleToolTip(local_x, local_y, mask) )
+ if (viewp->handleToolTip(local_x, local_y, mask)
+ || viewp->blockMouseEvent(local_x, local_y))
{
- if (sDebugMouseHandling)
- {
- sMouseHandlerMessage = std::string("/") + viewp->mName + sMouseHandlerMessage;
- }
+ // This is NOT event recording related
+ viewp->logMouseEvent();
+ return viewp;
+ }
+ }
+ return NULL;
+}
- handled_view = viewp;
- break;
+LLView* LLView::childrenHandleDragAndDrop(S32 x, S32 y, MASK mask,
+ BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ std::string& tooltip_msg)
+{
+ // default to not accepting drag and drop, will be overridden by handler
+ *accept = ACCEPT_NO;
+
+ BOOST_FOREACH(LLView* viewp, mChildList)
+ {
+ S32 local_x = x - viewp->getRect().mLeft;
+ S32 local_y = y - viewp->getRect().mBottom;
+ if( !viewp->visibleEnabledAndContains(local_x, local_y))
+ {
+ continue;
}
- if (viewp->blockMouseEvent(local_x, local_y))
+ // Differs from childrenHandleMouseEvent() simply in that this virtual
+ // method call diverges pretty radically from the usual (x, y, int).
+ if (viewp->handleDragAndDrop(local_x, local_y, mask, drop,
+ cargo_type,
+ cargo_data,
+ accept,
+ tooltip_msg)
+ || viewp->blockMouseEvent(local_x, local_y))
{
- handled_view = viewp;
- break;
+ return viewp;
}
}
- return handled_view;
+ return NULL;
}
+LLView* LLView::childrenHandleHover(S32 x, S32 y, MASK mask)
+{
+ BOOST_FOREACH(LLView* viewp, mChildList)
+ {
+ S32 local_x = x - viewp->getRect().mLeft;
+ S32 local_y = y - viewp->getRect().mBottom;
+ if(!viewp->visibleEnabledAndContains(local_x, local_y))
+ {
+ continue;
+ }
+
+ // This call differentiates this method from childrenHandleMouseEvent().
+ LLUI::sWindow->setCursor(viewp->getHoverCursor());
+
+ if (viewp->handleHover(local_x, local_y, mask)
+ || viewp->blockMouseEvent(local_x, local_y))
+ {
+ // This is NOT event recording related
+ viewp->logMouseEvent();
+ return viewp;
+ }
+ }
+ return NULL;
+}
-LLView* LLView::childFromPoint(S32 x, S32 y)
+LLView* LLView::childFromPoint(S32 x, S32 y, bool recur)
{
- if (!getVisible() )
+ if (!getVisible())
return false;
- for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
+
+ BOOST_FOREACH(LLView* viewp, mChildList)
{
- 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() )
+ if (!viewp->visibleAndContains(local_x, local_y))
{
continue;
}
+ // Here we've found the first (frontmost) visible child at this level
+ // containing the specified point. Is the caller asking us to drill
+ // down and return the innermost leaf child at this point, or just the
+ // top-level child?
+ if (recur)
+ {
+ LLView* leaf(viewp->childFromPoint(local_x, local_y, recur));
+ // Maybe viewp is already a leaf LLView, or maybe it has children
+ // but this particular (x, y) point falls between them. If the
+ // recursive call returns non-NULL, great, use that; else just use
+ // viewp.
+ return leaf? leaf : viewp;
+ }
return viewp;
}
@@ -708,18 +842,18 @@ BOOL LLView::handleToolTip(S32 x, S32 y, MASK mask)
// parents provide tooltips first, which are optionally
// overridden by children, in case child is mouse_opaque
- if (!mToolTipMsg.empty())
+ std::string tooltip = getToolTip();
+ if (!tooltip.empty())
{
// 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" );
+ ? LLUI::sSettingGroups["config"]->getF32( "ToolTipFastDelay" )
+ : LLUI::sSettingGroups["config"]->getF32( "ToolTipDelay" );
LLToolTipMgr::instance().show(LLToolTip::Params()
- .message(mToolTipMsg)
- .sticky_rect(calcScreenRect())
- .delay_time(timeout));
-
+ .message(tooltip)
+ .sticky_rect(calcScreenRect())
+ .delay_time(timeout));
handled = TRUE;
}
@@ -746,10 +880,12 @@ BOOL LLView::handleKey(KEY key, MASK mask, BOOL called_from_parent)
if (!handled)
{
+ // For event logging we don't care which widget handles it
+ // So we capture the key at the end of this function once we know if it was handled
handled = handleKeyHere( key, mask );
- if (handled && LLView::sDebugKeys)
+ if (handled)
{
- llinfos << "Key handled by " << getName() << llendl;
+ LL_DEBUGS() << "Key handled by " << getName() << LL_ENDL;
}
}
}
@@ -786,7 +922,7 @@ BOOL LLView::handleUnicodeChar(llwchar uni_char, BOOL called_from_parent)
handled = handleUnicodeCharHere(uni_char);
if (handled && LLView::sDebugKeys)
{
- llinfos << "Unicode key handled by " << getName() << llendl;
+ LL_INFOS() << "Unicode key handled by " << getName() << LL_ENDL;
}
}
}
@@ -797,6 +933,11 @@ BOOL LLView::handleUnicodeChar(llwchar uni_char, BOOL called_from_parent)
handled = mParentView->handleUnicodeChar(uni_char, FALSE);
}
+ if (handled)
+ {
+ LLViewerEventRecorder::instance().logKeyUnicodeEvent(uni_char);
+ }
+
return handled;
}
@@ -815,45 +956,6 @@ BOOL LLView::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
return childrenHandleDragAndDrop( x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg) != NULL;
}
-LLView* LLView::childrenHandleDragAndDrop(S32 x, S32 y, MASK mask,
- BOOL drop,
- EDragAndDropType cargo_type,
- void* cargo_data,
- EAcceptance* accept,
- std::string& tooltip_msg)
-{
- LLView* handled_view = NULL;
- 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())
- {
- 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;
-}
-
void LLView::onMouseCaptureLost()
{
}
@@ -865,12 +967,16 @@ BOOL LLView::hasMouseCapture()
BOOL LLView::handleMouseUp(S32 x, S32 y, MASK mask)
{
- return childrenHandleMouseUp( x, y, mask ) != NULL;
+ LLView* r = childrenHandleMouseUp( x, y, mask );
+
+ return (r!=NULL);
}
BOOL LLView::handleMouseDown(S32 x, S32 y, MASK mask)
{
- return childrenHandleMouseDown( x, y, mask ) != NULL;
+ LLView* r= childrenHandleMouseDown(x, y, mask );
+
+ return (r!=NULL);
}
BOOL LLView::handleDoubleClick(S32 x, S32 y, MASK mask)
@@ -903,391 +1009,57 @@ BOOL LLView::handleMiddleMouseUp(S32 x, S32 y, MASK mask)
return childrenHandleMiddleMouseUp( x, y, mask ) != NULL;
}
-
LLView* LLView::childrenHandleScrollWheel(S32 x, S32 y, S32 clicks)
{
- LLView* handled_view = NULL;
- if (getVisible() && getEnabled() )
- {
- 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())
- {
- continue;
- }
-
- if (viewp->handleScrollWheel( local_x, local_y, clicks ))
- {
- if (sDebugMouseHandling)
- {
- sMouseHandlerMessage = std::string("/") + viewp->mName + sMouseHandlerMessage;
- }
-
- handled_view = viewp;
- break;
- }
- }
- }
- return handled_view;
-}
-
-LLView* LLView::childrenHandleHover(S32 x, S32 y, MASK mask)
-{
- LLView* handled_view = NULL;
- if (getVisible() && getEnabled() )
- {
- 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())
- {
- continue;
- }
-
- if (viewp->handleHover(local_x, local_y, mask) )
- {
- if (sDebugMouseHandling)
- {
- 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;
+ return childrenHandleMouseEvent(&LLView::handleScrollWheel, x, y, clicks, false);
}
// Called during downward traversal
LLView* LLView::childrenHandleKey(KEY key, MASK mask)
{
- LLView* handled_view = NULL;
-
- if ( getVisible() && getEnabled() )
- {
- for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
- {
- LLView* viewp = *child_it;
- if (viewp->handleKey(key, mask, TRUE))
- {
- if (LLView::sDebugKeys)
- {
- llinfos << "Key handled by " << viewp->getName() << llendl;
- }
- handled_view = viewp;
- break;
- }
- }
- }
-
- return handled_view;
+ return childrenHandleCharEvent("Key", &LLView::handleKey, key, mask);
}
// Called during downward traversal
LLView* LLView::childrenHandleUnicodeChar(llwchar uni_char)
{
- LLView* handled_view = NULL;
-
- if ( getVisible() && getEnabled() )
- {
- for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
- {
- LLView* viewp = *child_it;
- if (viewp->handleUnicodeChar(uni_char, TRUE))
- {
- if (LLView::sDebugKeys)
- {
- llinfos << "Unicode character handled by " << viewp->getName() << llendl;
- }
- handled_view = viewp;
- break;
- }
- }
- }
-
- return handled_view;
+ return childrenHandleCharEvent("Unicode character", &LLView::handleUnicodeCharWithDummyMask,
+ uni_char, MASK_NONE);
}
LLView* LLView::childrenHandleMouseDown(S32 x, S32 y, MASK mask)
{
- LLView* handled_view = NULL;
-
- 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())
- {
- continue;
- }
-
- if(viewp->handleMouseDown( local_x, local_y, mask ))
- {
- if (sDebugMouseHandling)
- {
- sMouseHandlerMessage = std::string("/") + viewp->mName + sMouseHandlerMessage;
- }
- handled_view = viewp;
- break;
- }
-
- if(viewp->blockMouseEvent(local_x, local_y))
- {
- handled_view = viewp;
- break;
- }
- }
- return handled_view;
+ return childrenHandleMouseEvent(&LLView::handleMouseDown, x, y, mask);
}
LLView* LLView::childrenHandleRightMouseDown(S32 x, S32 y, MASK mask)
{
- LLView* handled_view = NULL;
-
- if (getVisible() && getEnabled() )
- {
- 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())
- {
- continue;
- }
-
- if (viewp->handleRightMouseDown( local_x, local_y, mask ))
- {
- if (sDebugMouseHandling)
- {
- sMouseHandlerMessage = std::string("/") + viewp->mName + sMouseHandlerMessage;
- }
-
- handled_view = viewp;
- break;
- }
-
- if (viewp->blockMouseEvent(local_x, local_y))
- {
- handled_view = viewp;
- break;
- }
- }
- }
- return handled_view;
+ return childrenHandleMouseEvent(&LLView::handleRightMouseDown, x, y, mask);
}
LLView* LLView::childrenHandleMiddleMouseDown(S32 x, S32 y, MASK mask)
{
- LLView* handled_view = NULL;
-
- if (getVisible() && getEnabled() )
- {
- 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())
- {
- continue;
- }
-
- if(viewp->handleMiddleMouseDown( local_x, local_y, mask ))
- {
- if (sDebugMouseHandling)
- {
- sMouseHandlerMessage = std::string("/") + viewp->mName + sMouseHandlerMessage;
- }
- handled_view = viewp;
- break;
- }
-
- if (viewp->blockMouseEvent(local_x, local_y))
- {
- handled_view = viewp;
- break;
- }
- }
- }
- return handled_view;
+ return childrenHandleMouseEvent(&LLView::handleMiddleMouseDown, x, y, mask);
}
LLView* LLView::childrenHandleDoubleClick(S32 x, S32 y, MASK mask)
{
- LLView* handled_view = NULL;
-
- if (getVisible() && getEnabled() )
- {
- 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())
- {
- continue;
- }
-
- if (viewp->handleDoubleClick( local_x, local_y, mask ))
- {
- if (sDebugMouseHandling)
- {
- sMouseHandlerMessage = std::string("/") + viewp->mName + sMouseHandlerMessage;
- }
- handled_view = viewp;
- break;
- }
-
- if (viewp->blockMouseEvent(local_x, local_y))
- {
- handled_view = viewp;
- break;
- }
- }
- }
- return handled_view;
+ return childrenHandleMouseEvent(&LLView::handleDoubleClick, x, y, mask);
}
LLView* LLView::childrenHandleMouseUp(S32 x, S32 y, MASK mask)
{
- LLView* handled_view = NULL;
- if( getVisible() && getEnabled() )
- {
- 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())
- {
- continue;
- }
-
- if (viewp->handleMouseUp( local_x, local_y, mask ))
- {
- if (sDebugMouseHandling)
- {
- sMouseHandlerMessage = std::string("/") + viewp->mName + sMouseHandlerMessage;
- }
- handled_view = viewp;
- break;
- }
-
- if (viewp->blockMouseEvent(local_x, local_y))
- {
- handled_view = viewp;
- break;
- }
- }
- }
- return handled_view;
+ return childrenHandleMouseEvent(&LLView::handleMouseUp, x, y, mask);
}
LLView* LLView::childrenHandleRightMouseUp(S32 x, S32 y, MASK mask)
{
- LLView* handled_view = NULL;
- if( getVisible() && getEnabled() )
- {
- 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() )
- {
- continue;
- }
-
- if(viewp->handleRightMouseUp( local_x, local_y, mask ))
- {
- if (sDebugMouseHandling)
- {
- sMouseHandlerMessage = std::string("/") + viewp->mName + sMouseHandlerMessage;
- }
- handled_view = viewp;
- break;
- }
-
- if(viewp->blockMouseEvent(local_x, local_y))
- {
- handled_view = viewp;
- break;
- }
- }
- }
- return handled_view;
+ return childrenHandleMouseEvent(&LLView::handleRightMouseUp, x, y, mask);
}
LLView* LLView::childrenHandleMiddleMouseUp(S32 x, S32 y, MASK mask)
{
- LLView* handled_view = NULL;
- if( getVisible() && getEnabled() )
- {
- 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())
- {
- continue;
- }
-
- if(viewp->handleMiddleMouseUp( local_x, local_y, mask ))
- {
- if (sDebugMouseHandling)
- {
- sMouseHandlerMessage = std::string("/") + viewp->mName + sMouseHandlerMessage;
- }
- handled_view = viewp;
- break;
- }
-
- if (viewp->blockMouseEvent(local_x, local_y))
- {
- handled_view = viewp;
- break;
- }
- }
- }
- return handled_view;
+ return childrenHandleMouseEvent(&LLView::handleMiddleMouseUp, x, y, mask);
}
void LLView::draw()
@@ -1299,26 +1071,31 @@ void LLView::drawChildren()
{
if (!mChildList.empty())
{
- LLRect rootRect = getRootView()->getRect();
- LLRect screenRect;
-
+ LLView* rootp = LLUI::getRootView();
++sDepth;
for (child_list_reverse_iter_t child_iter = mChildList.rbegin(); child_iter != mChildList.rend();) // ++child_iter)
{
child_list_reverse_iter_t child = child_iter++;
LLView *viewp = *child;
+
+ if (viewp == NULL)
+ {
+ continue;
+ }
if (viewp->getVisible() && viewp->getRect().isValid())
{
- // Only draw views that are within the root view
- localRectToScreen(viewp->getRect(),&screenRect);
- if ( rootRect.overlaps(screenRect) && LLUI::sDirtyRect.overlaps(screenRect))
+ LLRect screen_rect = viewp->calcScreenRect();
+ if ( rootp->getLocalRect().overlaps(screen_rect) && LLUI::sDirtyRect.overlaps(screen_rect))
{
LLUI::pushMatrix();
{
- LLUI::translate((F32)viewp->getRect().mLeft, (F32)viewp->getRect().mBottom, 0.f);
+ LLUI::translate((F32)viewp->getRect().mLeft, (F32)viewp->getRect().mBottom);
+ // flag the fact we are in draw here, in case overridden draw() method attempts to remove this widget
+ viewp->mInDraw = true;
viewp->draw();
+ viewp->mInDraw = false;
if (sDebugRects)
{
@@ -1327,7 +1104,7 @@ void LLView::drawChildren()
// Check for bogus rectangle
if (!getRect().isValid())
{
- llwarns << "Bogus rectangle for " << getName() << " with " << mRect << llendl;
+ LL_WARNS() << "Bogus rectangle for " << getName() << " with " << mRect << LL_ENDL;
}
}
}
@@ -1367,7 +1144,7 @@ void LLView::drawDebugRect()
if (getUseBoundingRect())
{
- LLUI::translate((F32)mBoundingRect.mLeft - (F32)mRect.mLeft, (F32)mBoundingRect.mBottom - (F32)mRect.mBottom, 0.f);
+ LLUI::translate((F32)mBoundingRect.mLeft - (F32)mRect.mLeft, (F32)mBoundingRect.mBottom - (F32)mRect.mBottom);
}
LLRect debug_rect = getUseBoundingRect() ? mBoundingRect : mRect;
@@ -1412,11 +1189,24 @@ void LLView::drawDebugRect()
&& preview_iter == sPreviewHighlightedElements.end()
&& sDebugRectsShowNames)
{
- //char temp[256];
S32 x, y;
gGL.color4fv( border_color.mV );
- x = debug_rect.getWidth()/2;
- y = debug_rect.getHeight()/2;
+
+ x = debug_rect.getWidth() / 2;
+
+ S32 rect_height = debug_rect.getHeight();
+ S32 lines = rect_height / LINE_HEIGHT + 1;
+
+ S32 depth = 0;
+ LLView * viewp = this;
+ while (NULL != viewp)
+ {
+ viewp = viewp->getParent();
+ depth++;
+ }
+
+ y = rect_height - LINE_HEIGHT * (depth % lines + 1);
+
std::string debug_text = llformat("%s (%d x %d)", getName().c_str(),
debug_rect.getWidth(), debug_rect.getHeight());
LLFontGL::getFontSansSerifSmall()->renderUTF8(debug_text, 0, (F32)x, (F32)y, border_color,
@@ -1436,10 +1226,10 @@ void LLView::drawChild(LLView* childp, S32 x_offset, S32 y_offset, BOOL force_dr
if ((childp->getVisible() && childp->getRect().isValid())
|| force_draw)
{
- glMatrixMode(GL_MODELVIEW);
+ gGL.matrixMode(LLRender::MM_MODELVIEW);
LLUI::pushMatrix();
{
- LLUI::translate((F32)childp->getRect().mLeft + x_offset, (F32)childp->getRect().mBottom + y_offset, 0.f);
+ LLUI::translate((F32)childp->getRect().mLeft + x_offset, (F32)childp->getRect().mBottom + y_offset);
childp->draw();
}
LLUI::popMatrix();
@@ -1463,9 +1253,10 @@ void LLView::reshape(S32 width, S32 height, BOOL called_from_parent)
mRect.mTop = getRect().mBottom + height;
// move child views according to reshape flags
- for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
+ BOOST_FOREACH(LLView* viewp, mChildList)
{
- LLView* viewp = *child_it;
+ if (viewp != NULL)
+ {
LLRect child_rect( viewp->mRect );
if (viewp->followsRight() && viewp->followsLeft())
@@ -1509,9 +1300,13 @@ void LLView::reshape(S32 width, S32 height, BOOL called_from_parent)
S32 delta_x = child_rect.mLeft - viewp->getRect().mLeft;
S32 delta_y = child_rect.mBottom - viewp->getRect().mBottom;
viewp->translate( delta_x, delta_y );
- viewp->reshape(child_rect.getWidth(), child_rect.getHeight());
+ if (child_rect.getWidth() != viewp->getRect().getWidth() || child_rect.getHeight() != viewp->getRect().getHeight())
+ {
+ viewp->reshape(child_rect.getWidth(), child_rect.getHeight());
+ }
}
}
+ }
if (!called_from_parent)
{
@@ -1528,10 +1323,8 @@ LLRect LLView::calcBoundingRect()
{
LLRect local_bounding_rect = LLRect::null;
- child_list_const_iter_t child_it;
- for ( child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
+ BOOST_FOREACH(LLView* childp, mChildList)
{
- LLView* childp = *child_it;
// ignore invisible and "top" children when calculating bounding rect
// such as combobox popups
if (!childp->getVisible() || childp == gFocusMgr.getTopCtrl())
@@ -1658,15 +1451,19 @@ BOOL LLView::hasAncestor(const LLView* parentp) const
BOOL LLView::childHasKeyboardFocus( const std::string& childname ) const
{
- LLView *child = findChildView(childname, TRUE);
- if (child)
- {
- return gFocusMgr.childHasKeyboardFocus(child);
- }
- else
+ LLView *focus = dynamic_cast<LLView *>(gFocusMgr.getKeyboardFocus());
+
+ while (focus != NULL)
{
- return FALSE;
+ if (focus->getName() == childname)
+ {
+ return TRUE;
+ }
+
+ focus = focus->getParent();
}
+
+ return FALSE;
}
//-----------------------------------------------------------------------------
@@ -1684,19 +1481,17 @@ LLView* LLView::getChildView(const std::string& name, BOOL recurse) const
return getChild<LLView>(name, recurse);
}
-static LLFastTimer::DeclareTimer FTM_FIND_VIEWS("Find Widgets");
+static LLTrace::BlockTimerStatHandle FTM_FIND_VIEWS("Find Widgets");
LLView* LLView::findChildView(const std::string& name, BOOL recurse) const
{
- LLFastTimer ft(FTM_FIND_VIEWS);
+ LL_RECORD_BLOCK_TIME(FTM_FIND_VIEWS);
//richard: should we allow empty names?
//if(name.empty())
// return NULL;
- child_list_const_iter_t child_it;
// Look for direct children *first*
- for ( child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
+ BOOST_FOREACH(LLView* childp, mChildList)
{
- LLView* childp = *child_it;
llassert(childp);
if (childp->getName() == name)
{
@@ -1706,9 +1501,8 @@ LLView* LLView::findChildView(const std::string& name, BOOL recurse) const
if (recurse)
{
// Look inside each child as well.
- for ( child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
+ BOOST_FOREACH(LLView* childp, mChildList)
{
- LLView* childp = *child_it;
llassert(childp);
LLView* viewp = childp->findChildView(name, recurse);
if ( viewp )
@@ -1756,16 +1550,18 @@ void LLView::screenPointToLocal(S32 screen_x, S32 screen_y, S32* local_x, S32* l
void LLView::localPointToScreen(S32 local_x, S32 local_y, S32* screen_x, S32* screen_y) const
{
- *screen_x = local_x + getRect().mLeft;
- *screen_y = local_y + getRect().mBottom;
+ *screen_x = local_x;
+ *screen_y = local_y;
const LLView* cur = this;
- while( cur->mParentView )
+ do
{
+ LLRect cur_rect = cur->getRect();
+ *screen_x += cur_rect.mLeft;
+ *screen_y += cur_rect.mBottom;
cur = cur->mParentView;
- *screen_x += cur->getRect().mLeft;
- *screen_y += cur->getRect().mBottom;
}
+ while( cur );
}
void LLView::screenRectToLocal(const LLRect& screen, LLRect* local) const
@@ -1825,67 +1621,31 @@ LLView* LLView::findNextSibling(LLView* child)
return (next_it != mChildList.end()) ? *next_it : NULL;
}
-void LLView::deleteViewByHandle(LLHandle<LLView> handle)
-{
- LLView* viewp = handle.get();
- delete viewp;
-}
-
-
-LLCoordGL getNeededTranslation(const LLRect& input, const LLRect& constraint, BOOL allow_partial_outside)
+LLCoordGL getNeededTranslation(const LLRect& input, const LLRect& constraint, S32 min_overlap_pixels)
{
LLCoordGL delta;
- if (allow_partial_outside)
- {
- const S32 KEEP_ONSCREEN_PIXELS = 16;
+ const S32 KEEP_ONSCREEN_PIXELS_WIDTH = llmin(min_overlap_pixels, input.getWidth());
+ const S32 KEEP_ONSCREEN_PIXELS_HEIGHT = llmin(min_overlap_pixels, input.getHeight());
- if( input.mRight - KEEP_ONSCREEN_PIXELS < constraint.mLeft )
- {
- delta.mX = constraint.mLeft - (input.mRight - KEEP_ONSCREEN_PIXELS);
- }
- else
- if( input.mLeft + KEEP_ONSCREEN_PIXELS > constraint.mRight )
- {
- delta.mX = constraint.mRight - (input.mLeft + KEEP_ONSCREEN_PIXELS);
- }
+ if( input.mRight - KEEP_ONSCREEN_PIXELS_WIDTH < constraint.mLeft )
+ {
+ delta.mX = constraint.mLeft - (input.mRight - KEEP_ONSCREEN_PIXELS_WIDTH);
+ }
+ else if( input.mLeft + KEEP_ONSCREEN_PIXELS_WIDTH > constraint.mRight )
+ {
+ delta.mX = constraint.mRight - (input.mLeft + KEEP_ONSCREEN_PIXELS_WIDTH);
+ }
- if( input.mTop > constraint.mTop )
- {
- delta.mY = constraint.mTop - input.mTop;
- }
- else
- if( input.mTop - KEEP_ONSCREEN_PIXELS < constraint.mBottom )
- {
- delta.mY = constraint.mBottom - (input.mTop - KEEP_ONSCREEN_PIXELS);
- }
+ if( input.mTop > constraint.mTop )
+ {
+ delta.mY = constraint.mTop - input.mTop;
}
else
+ if( input.mTop - KEEP_ONSCREEN_PIXELS_HEIGHT < constraint.mBottom )
{
- if( input.mLeft < constraint.mLeft )
- {
- delta.mX = constraint.mLeft - input.mLeft;
- }
- else
- if( input.mRight > constraint.mRight )
- {
- delta.mX = constraint.mRight - input.mRight;
- // compensate for left edge possible going off screen
- delta.mX += llmax( 0, input.getWidth() - constraint.getWidth() );
- }
-
- if( input.mTop > constraint.mTop )
- {
- delta.mY = constraint.mTop - input.mTop;
- }
- else
- if( input.mBottom < constraint.mBottom )
- {
- delta.mY = constraint.mBottom - input.mBottom;
- // compensate for top edge possible going off screen
- delta.mY -= llmax( 0, input.getHeight() - constraint.getHeight() );
- }
+ delta.mY = constraint.mBottom - (input.mTop - KEEP_ONSCREEN_PIXELS_HEIGHT);
}
return delta;
@@ -1894,9 +1654,9 @@ LLCoordGL getNeededTranslation(const LLRect& input, const LLRect& constraint, BO
// 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 )
+BOOL LLView::translateIntoRect(const LLRect& constraint, S32 min_overlap_pixels)
{
- LLCoordGL translation = getNeededTranslation(getRect(), constraint, allow_partial_outside);
+ LLCoordGL translation = getNeededTranslation(getRect(), constraint, min_overlap_pixels);
if (translation.mX != 0 || translation.mY != 0)
{
@@ -1908,9 +1668,9 @@ BOOL LLView::translateIntoRect(const LLRect& constraint, BOOL allow_partial_outs
// 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 )
+BOOL LLView::translateIntoRectWithExclusion( const LLRect& inside, const LLRect& exclude, S32 min_overlap_pixels)
{
- LLCoordGL translation = getNeededTranslation(getRect(), inside, allow_partial_outside);
+ LLCoordGL translation = getNeededTranslation(getRect(), inside, min_overlap_pixels);
if (translation.mX != 0 || translation.mY != 0)
{
@@ -1958,7 +1718,7 @@ void LLView::centerWithin(const LLRect& bounds)
translate( left - getRect().mLeft, bottom - getRect().mBottom );
}
-BOOL LLView::localPointToOtherView( S32 x, S32 y, S32 *other_x, S32 *other_y, LLView* other_view) const
+BOOL LLView::localPointToOtherView( S32 x, S32 y, S32 *other_x, S32 *other_y, const LLView* other_view) const
{
const LLView* cur_view = this;
const LLView* root_view = NULL;
@@ -2001,7 +1761,7 @@ BOOL LLView::localPointToOtherView( S32 x, S32 y, S32 *other_x, S32 *other_y, LL
return FALSE;
}
-BOOL LLView::localRectToOtherView( const LLRect& local, LLRect* other, LLView* other_view ) const
+BOOL LLView::localRectToOtherView( const LLRect& local, LLRect* other, const LLView* other_view ) const
{
LLRect cur_rect = local;
const LLView* cur_view = this;
@@ -2040,15 +1800,63 @@ BOOL LLView::localRectToOtherView( const LLRect& local, LLRect* other, LLView* o
return FALSE;
}
+
+class CompareByTabOrder
+{
+public:
+ CompareByTabOrder(const LLView::child_tab_order_t& order, S32 default_tab_group = 0)
+ : mTabOrder(order),
+ mDefaultTabGroup(default_tab_group)
+ {}
+ virtual ~CompareByTabOrder() {}
+
+ // This method compares two LLViews by the tab order specified in the comparator object. The
+ // code for this is a little convoluted because each argument can have four states:
+ // 1) not a control, 2) a control but not in the tab order, 3) a control in the tab order, 4) null
+ bool operator() (const LLView* const a, const LLView* const b) const
+ {
+ S32 a_group = 0, b_group = 0;
+ if(!a) return false;
+ if(!b) return true;
+
+ LLView::child_tab_order_const_iter_t a_found = mTabOrder.find(a), b_found = mTabOrder.find(b);
+ if(a_found != mTabOrder.end())
+ {
+ a_group = a_found->second;
+ }
+ if(b_found != mTabOrder.end())
+ {
+ b_group = b_found->second;
+ }
+
+ if(a_group < mDefaultTabGroup && b_group >= mDefaultTabGroup) return true;
+ if(b_group < mDefaultTabGroup && a_group >= mDefaultTabGroup) return false;
+ return a_group > b_group; // sort correctly if they're both on the same side of the default tab groupreturn a > b;
+ }
+private:
+ // ok to store a reference, as this should only be allocated on stack during view query operations
+ const LLView::child_tab_order_t& mTabOrder;
+ const S32 mDefaultTabGroup;
+};
+
+class SortByTabOrder : public LLQuerySorter, public LLSingleton<SortByTabOrder>
+{
+ /*virtual*/ void sort(LLView * parent, LLView::child_list_t &children) const
+ {
+ children.sort(CompareByTabOrder(parent->getTabOrder(), parent->getDefaultTabGroup()));
+ }
+};
+
// static
-const LLCtrlQuery & LLView::getTabOrderQuery()
+const LLViewQuery & LLView::getTabOrderQuery()
{
- static LLCtrlQuery query;
+ static LLViewQuery query;
if(query.getPreFilters().size() == 0) {
query.addPreFilter(LLVisibleFilter::getInstance());
query.addPreFilter(LLEnabledFilter::getInstance());
query.addPreFilter(LLTabStopFilter::getInstance());
query.addPostFilter(LLLeavesFilter::getInstance());
+ query.setSorter(SortByTabOrder::getInstance());
}
return query;
}
@@ -2063,9 +1871,9 @@ class LLFocusRootsFilter : public LLQueryFilter, public LLSingleton<LLFocusRoots
};
// static
-const LLCtrlQuery & LLView::getFocusRootsQuery()
+const LLViewQuery & LLView::getFocusRootsQuery()
{
- static LLCtrlQuery query;
+ static LLViewQuery query;
if(query.getPreFilters().size() == 0) {
query.addPreFilter(LLVisibleFilter::getInstance());
query.addPreFilter(LLEnabledFilter::getInstance());
@@ -2078,7 +1886,10 @@ const LLCtrlQuery & LLView::getFocusRootsQuery()
void LLView::setShape(const LLRect& new_rect, bool by_user)
{
- handleReshape(new_rect, by_user);
+ if (new_rect != getRect())
+ {
+ handleReshape(new_rect, by_user);
+ }
}
void LLView::handleReshape(const LLRect& new_rect, bool by_user)
@@ -2214,7 +2025,7 @@ LLView* LLView::findSnapEdge(S32& new_edge_val, const LLCoordGL& mouse_dir, ESna
}
break;
default:
- llerrs << "Invalid snap edge" << llendl;
+ LL_ERRS() << "Invalid snap edge" << LL_ENDL;
}
}
@@ -2316,7 +2127,7 @@ LLView* LLView::findSnapEdge(S32& new_edge_val, const LLCoordGL& mouse_dir, ESna
}
break;
default:
- llerrs << "Invalid snap edge" << llendl;
+ LL_ERRS() << "Invalid snap edge" << LL_ENDL;
}
}
}
@@ -2353,10 +2164,6 @@ LLControlVariable *LLView::findControl(const std::string& name)
return control_group.getControl(name);
}
-const S32 FLOATER_H_MARGIN = 15;
-const S32 MIN_WIDGET_HEIGHT = 10;
-const S32 VPAD = 4;
-
void LLView::initFromParams(const LLView::Params& params)
{
LLRect required_rect = getRequiredRect();
@@ -2468,110 +2275,163 @@ static bool get_last_child_rect(LLView* parent, LLRect *rect)
}
//static
-void LLView::applyXUILayout(LLView::Params& p, LLView* parent)
+void LLView::applyXUILayout(LLView::Params& p, LLView* parent, LLRect layout_rect)
{
+ if (!parent) return;
+
const S32 VPAD = 4;
const S32 MIN_WIDGET_HEIGHT = 10;
// *NOTE: This will confuse export of floater/panel coordinates unless
// the default is also "topleft". JC
- if (p.layout().empty() && parent)
+ if (p.layout().empty())
{
p.layout = parent->getLayout();
}
- if (parent)
+ if (layout_rect.isEmpty())
{
- LLRect parent_rect = parent->getLocalRect();
- // overwrite uninitialized rect params, using context
- LLRect last_rect = parent->getLocalRect();
+ layout_rect = parent->getLocalRect();
+ }
- bool layout_topleft = (p.layout() == "topleft");
+ // overwrite uninitialized rect params, using context
+ LLRect default_rect = parent->getLocalRect();
- // 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();
+ 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 = p.rect.left + ((p.rect.left >= 0) ? layout_rect.mLeft : layout_rect.mRight);
+ }
+ if (p.rect.right.isProvided())
+ {
+ p.rect.right = p.rect.right + ((p.rect.right >= 0) ? layout_rect.mLeft : layout_rect.mRight);
+ }
+ if (p.rect.bottom.isProvided())
+ {
+ p.rect.bottom = p.rect.bottom + ((p.rect.bottom >= 0) ? layout_rect.mBottom : layout_rect.mTop);
if (layout_topleft)
{
//invert top to bottom
- if (p.rect.top.isProvided()) p.rect.top = parent_rect.getHeight() - p.rect.top;
- if (p.rect.bottom.isProvided()) p.rect.bottom = parent_rect.getHeight() - p.rect.bottom;
+ p.rect.bottom = layout_rect.mBottom + layout_rect.mTop - p.rect.bottom;
}
-
- // DEPRECATE: automatically fall back to height of MIN_WIDGET_HEIGHT pixels
- if (!p.rect.height.isProvided() && !p.rect.top.isProvided() && p.rect.height == 0)
+ }
+ if (p.rect.top.isProvided())
+ {
+ p.rect.top = p.rect.top + ((p.rect.top >= 0) ? layout_rect.mBottom : layout_rect.mTop);
+ if (layout_topleft)
{
- p.rect.height = MIN_WIDGET_HEIGHT;
+ //invert top to bottom
+ p.rect.top = layout_rect.mBottom + layout_rect.mTop - p.rect.top;
}
+ }
- last_rect.translate(0, last_rect.getHeight());
+ // DEPRECATE: automatically fall back to height of MIN_WIDGET_HEIGHT pixels
+ if (!p.rect.height.isProvided() && !p.rect.top.isProvided() && p.rect.height == 0)
+ {
+ p.rect.height = MIN_WIDGET_HEIGHT;
+ }
- // If there was a recently constructed child, use its rectangle
- get_last_child_rect(parent, &last_rect);
+ default_rect.translate(0, default_rect.getHeight());
- if (layout_topleft)
- {
- p.bottom_delta.setIfNotProvided(0, false);
+ // If there was a recently constructed child, use its rectangle
+ get_last_child_rect(parent, &default_rect);
- // Invert the sense of bottom_delta for topleft layout
- if (p.bottom_delta.isProvided())
- {
- p.bottom_delta = -p.bottom_delta;
- }
- else if (p.top_pad.isProvided())
- {
- p.bottom_delta = -(p.rect.height + p.top_pad);
- }
- else if (p.top_delta.isProvided())
- {
- p.bottom_delta =
- -(p.top_delta + p.rect.height - last_rect.getHeight());
- }
- else if (!p.bottom_delta.isProvided()
- && !p.left_delta.isProvided()
- && !p.top_pad.isProvided()
- && !p.left_pad.isProvided())
- {
- // set default position is just below last rect
- p.bottom_delta.set(-(p.rect.height + VPAD), false);
- }
+ if (layout_topleft)
+ {
+ // Invert the sense of bottom_delta for topleft layout
+ if (p.bottom_delta.isProvided())
+ {
+ p.bottom_delta = -p.bottom_delta;
+ }
+ else if (p.top_pad.isProvided())
+ {
+ p.bottom_delta = -(p.rect.height + p.top_pad);
+ }
+ else if (p.top_delta.isProvided())
+ {
+ p.bottom_delta =
+ -(p.top_delta + p.rect.height - default_rect.getHeight());
+ }
+ else if (!p.left_delta.isProvided()
+ && !p.left_pad.isProvided())
+ {
+ // set default position is just below last rect
+ p.bottom_delta.set(-(p.rect.height + VPAD), false);
+ }
+ else
+ {
+ p.bottom_delta.set(0, false);
+ }
- // default to same left edge
- p.left_delta.setIfNotProvided(0, false);
- if (p.left_pad.isProvided())
- {
- // left_pad is based on prior widget's right edge
- p.left_delta.set(p.left_pad + last_rect.getWidth(), false);
- }
+ // default to same left edge
+ if (!p.left_delta.isProvided())
+ {
+ p.left_delta.set(0, false);
+ }
+ if (p.left_pad.isProvided())
+ {
+ // left_pad is based on prior widget's right edge
+ p.left_delta.set(p.left_pad + default_rect.getWidth(), false);
+ }
- last_rect.translate(p.left_delta, p.bottom_delta);
+ default_rect.translate(p.left_delta, p.bottom_delta);
+ }
+ else
+ {
+ // set default position is just below last rect
+ if (!p.bottom_delta.isProvided())
+ {
+ p.bottom_delta.set(-(p.rect.height + VPAD), false);
}
- else
- {
- // set default position is just below last rect
- p.bottom_delta.setIfNotProvided(-(p.rect.height + VPAD), false);
- p.left_delta.setIfNotProvided(0, false);
- last_rect.translate(p.left_delta, p.bottom_delta);
+ if (!p.left_delta.isProvided())
+ {
+ p.left_delta.set(0, false);
}
+ default_rect.translate(p.left_delta, p.bottom_delta);
+ }
+
+ // this handles case where *both* x and x_delta are provided
+ // ignore x in favor of default x + x_delta
+ if (p.bottom_delta.isProvided()) p.rect.bottom.set(0, false);
+ if (p.left_delta.isProvided()) p.rect.left.set(0, false);
- // this handles case where *both* x and x_delta are provided
- // ignore x in favor of default x + x_delta
- if (p.bottom_delta.isProvided()) p.rect.bottom.set(0, false);
- if (p.left_delta.isProvided()) p.rect.left.set(0, false);
+ // selectively apply rectangle defaults, making sure that
+ // params are not flagged as having been "provided"
+ // as rect params are overconstrained and rely on provided flags
+ if (!p.rect.left.isProvided())
+ {
+ p.rect.left.set(default_rect.mLeft, false);
+ //HACK: get around the fact that setting a rect param component value won't invalidate the existing rect object value
+ p.rect.paramChanged(p.rect.left, true);
+ }
+ if (!p.rect.bottom.isProvided())
+ {
+ p.rect.bottom.set(default_rect.mBottom, false);
+ p.rect.paramChanged(p.rect.bottom, true);
+ }
+ if (!p.rect.top.isProvided())
+ {
+ p.rect.top.set(default_rect.mTop, false);
+ p.rect.paramChanged(p.rect.top, true);
+ }
+ if (!p.rect.right.isProvided())
+ {
+ p.rect.right.set(default_rect.mRight, false);
+ p.rect.paramChanged(p.rect.right, true);
- // selectively apply rectangle defaults, making sure that
- // params are not flagged as having been "provided"
- // as rect params are overconstrained and rely on provided flags
- p.rect.left.setIfNotProvided(last_rect.mLeft, false);
- p.rect.bottom.setIfNotProvided(last_rect.mBottom, false);
- p.rect.top.setIfNotProvided(last_rect.mTop, false);
- p.rect.right.setIfNotProvided(last_rect.mRight, false);
- p.rect.width.setIfNotProvided(last_rect.getWidth(), false);
- p.rect.height.setIfNotProvided(last_rect.getHeight(), false);
+ }
+ if (!p.rect.width.isProvided())
+ {
+ p.rect.width.set(default_rect.getWidth(), false);
+ p.rect.paramChanged(p.rect.width, true);
+ }
+ if (!p.rect.height.isProvided())
+ {
+ p.rect.height.set(default_rect.getHeight(), false);
+ p.rect.paramChanged(p.rect.height, true);
}
}
@@ -2590,7 +2450,7 @@ static S32 invert_vertical(S32 y, LLView* parent)
}
else
{
- llwarns << "Attempting to convert layout to top-left with no parent" << llendl;
+ LL_WARNS() << "Attempting to convert layout to top-left with no parent" << LL_ENDL;
return y;
}
}
@@ -2814,9 +2674,9 @@ S32 LLView::notifyParent(const LLSD& info)
bool LLView::notifyChildren(const LLSD& info)
{
bool ret = false;
- for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
+ BOOST_FOREACH(LLView* childp, mChildList)
{
- ret |= (*child_it)->notifyChildren(info);
+ ret = ret || childp->notifyChildren(info);
}
return ret;
}
@@ -2836,3 +2696,24 @@ const LLViewDrawContext& LLViewDrawContext::getCurrentContext()
return *sDrawContextStack.back();
}
+
+LLSD LLView::getInfo(void)
+{
+ LLSD info;
+ addInfo(info);
+ return info;
+}
+
+void LLView::addInfo(LLSD & info)
+{
+ info["path"] = getPathname();
+ info["class"] = typeid(*this).name();
+ info["visible"] = getVisible();
+ info["visible_chain"] = isInVisibleChain();
+ info["enabled"] = getEnabled();
+ info["enabled_chain"] = isInEnabledChain();
+ info["available"] = isAvailable();
+ LLRect rect(calcScreenRect());
+ info["rect"] = LLSDMap("left", rect.mLeft)("top", rect.mTop)
+ ("right", rect.mRight)("bottom", rect.mBottom);
+}
diff --git a/indra/llui/llview.h b/indra/llui/llview.h
index d2bbd663b8..7861c8f729 100644..100755
--- a/indra/llui/llview.h
+++ b/indra/llui/llview.h
@@ -34,6 +34,7 @@
#include "stdtypes.h"
#include "llcoord.h"
#include "llfontgl.h"
+#include "llhandle.h"
#include "llmortician.h"
#include "llmousehandler.h"
#include "llstring.h"
@@ -41,7 +42,6 @@
#include "llui.h"
#include "lluistring.h"
#include "llviewquery.h"
-#include "stdenums.h"
#include "lluistring.h"
#include "llcursortypes.h"
#include "lluictrlfactory.h"
@@ -49,6 +49,8 @@
#include "llfocusmgr.h"
#include <list>
+#include <boost/function.hpp>
+#include <boost/noncopyable.hpp>
class LLSD;
@@ -94,13 +96,18 @@ private:
static std::vector<LLViewDrawContext*> sDrawContextStack;
};
-class LLViewWidgetRegistry : public LLChildRegistry<LLViewWidgetRegistry>
-{};
-
-class LLView : public LLMouseHandler, public LLMortician, public LLFocusableElement
+class LLView
+: public LLMouseHandler, // handles mouse events
+ public LLFocusableElement, // handles keyboard events
+ public LLMortician, // lazy deletion
+ public LLHandleProvider<LLView>, // passes out weak references to self
+ public LLTrace::MemTrackable<LLView> // track memory usage
{
public:
- struct Follows : public LLInitParam::Choice<Follows>
+
+ enum EOrientation { HORIZONTAL, VERTICAL, ORIENTATION_COUNT };
+
+ struct Follows : public LLInitParam::ChoiceBlock<Follows>
{
Alternative<std::string> string;
Alternative<U32> flags;
@@ -149,7 +156,8 @@ public:
Params();
};
- typedef LLViewWidgetRegistry child_registry_t;
+ // most widgets are valid children of LLView
+ typedef LLDefaultChildRegistry child_registry_t;
void initFromParams(const LLView::Params&);
@@ -159,7 +167,7 @@ protected:
private:
// widgets in general are not copyable
- LLView(const LLView& other) {};
+ LLView(const LLView& other);
public:
//#if LL_DEBUG
static BOOL sIsDrawing;
@@ -192,12 +200,9 @@ public:
typedef child_list_t::reverse_iterator child_list_reverse_iter_t;
typedef child_list_t::const_reverse_iterator child_list_const_reverse_iter_t;
- typedef std::vector<class LLUICtrl *> ctrl_list_t;
-
- typedef std::pair<S32, S32> tab_order_t;
- typedef std::pair<LLUICtrl *, tab_order_t> tab_order_pair_t;
+ typedef std::pair<LLView *, S32> tab_order_pair_t;
// this structure primarily sorts by the tab group, secondarily by the insertion ordinal (lastly by the value of the pointer)
- typedef std::map<const LLUICtrl*, tab_order_t> child_tab_order_t;
+ typedef std::map<const LLView*, S32> child_tab_order_t;
typedef child_tab_order_t::iterator child_tab_order_iter_t;
typedef child_tab_order_t::const_iterator child_tab_order_const_iter_t;
typedef child_tab_order_t::reverse_iterator child_tab_order_reverse_iter_t;
@@ -239,12 +244,10 @@ public:
ECursorType getHoverCursor() { return mHoverCursor; }
- const std::string& getToolTip() const { return mToolTipMsg.getString(); }
+ virtual const std::string getToolTip() const { return mToolTipMsg.getString(); }
void sendChildToFront(LLView* child);
void sendChildToBack(LLView* child);
- void moveChildToFrontOfTabGroup(LLUICtrl* child);
- void moveChildToBackOfTabGroup(LLUICtrl* child);
virtual bool addChild(LLView* view, S32 tab_group = 0);
@@ -256,9 +259,7 @@ public:
virtual BOOL postBuild() { return TRUE; }
- const child_tab_order_t& getCtrlOrder() const { return mCtrlOrder; }
- ctrl_list_t getCtrlList() const;
- ctrl_list_t getCtrlListSorted() const;
+ const child_tab_order_t& getTabOrder() const { return mTabOrder; }
void setDefaultTabGroup(S32 d) { mDefaultTabGroup = d; }
S32 getDefaultTabGroup() const { return mDefaultTabGroup; }
@@ -287,7 +288,7 @@ public:
void setAllChildrenEnabled(BOOL b);
virtual void setVisible(BOOL visible);
- BOOL getVisible() const { return mVisible; }
+ const BOOL& getVisible() const { return mVisible; }
virtual void setEnabled(BOOL enabled);
BOOL getEnabled() const { return mEnabled; }
/// 'available' in this context means 'visible and enabled': in other
@@ -299,14 +300,12 @@ public:
virtual BOOL setLabelArg( const std::string& key, const LLStringExplicit& text );
- virtual void handleVisibilityChange ( BOOL new_visibility );
+ virtual void onVisibilityChange ( 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; }
BOOL followsLeft() const { return mReshapeFlags & FOLLOWS_LEFT; }
BOOL followsRight() const { return mReshapeFlags & FOLLOWS_RIGHT; }
@@ -367,8 +366,8 @@ public:
virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
virtual void translate( S32 x, S32 y );
void setOrigin( S32 x, S32 y ) { mRect.translate( x - mRect.mLeft, y - mRect.mBottom ); }
- BOOL translateIntoRect( const LLRect& constraint, BOOL allow_partial_outside );
- BOOL translateIntoRectWithExclusion( const LLRect& inside, const LLRect& exclude, BOOL allow_partial_outside );
+ BOOL translateIntoRect( const LLRect& constraint, S32 min_overlap_pixels = S32_MAX);
+ BOOL translateIntoRectWithExclusion( const LLRect& inside, const LLRect& exclude, S32 min_overlap_pixels = S32_MAX);
void centerWithin(const LLRect& bounds);
void setShape(const LLRect& new_rect, bool by_user = false);
@@ -406,8 +405,8 @@ public:
BOOL blockMouseEvent(S32 x, S32 y) const;
// See LLMouseHandler virtuals for screenPointToLocal and localPointToScreen
- BOOL localPointToOtherView( S32 x, S32 y, S32 *other_x, S32 *other_y, LLView* other_view) const;
- BOOL localRectToOtherView( const LLRect& local, LLRect* other, LLView* other_view ) const;
+ BOOL localPointToOtherView( S32 x, S32 y, S32 *other_x, S32 *other_y, const LLView* other_view) const;
+ BOOL localRectToOtherView( const LLRect& local, LLRect* other, const LLView* other_view ) const;
void screenRectToLocal( const LLRect& screen, LLRect* local ) const;
void localRectToScreen( const LLRect& local, LLRect* screen ) const;
@@ -430,18 +429,21 @@ public:
/*virtual*/ BOOL handleRightMouseUp(S32 x, S32 y, MASK mask);
/*virtual*/ BOOL handleToolTip(S32 x, S32 y, MASK mask);
- /*virtual*/ std::string getName() const;
+ /*virtual*/ const std::string& getName() const;
/*virtual*/ void onMouseCaptureLost();
/*virtual*/ BOOL hasMouseCapture();
/*virtual*/ void screenPointToLocal(S32 screen_x, S32 screen_y, S32* local_x, S32* local_y) const;
/*virtual*/ void localPointToScreen(S32 local_x, S32 local_y, S32* screen_x, S32* screen_y) const;
- virtual LLView* childFromPoint(S32 x, S32 y);
+ virtual LLView* childFromPoint(S32 x, S32 y, bool recur=false);
// view-specific handlers
virtual void onMouseEnter(S32 x, S32 y, MASK mask);
virtual void onMouseLeave(S32 x, S32 y, MASK mask);
+ std::string getPathname() const;
+ // static method handles NULL pointer too
+ static std::string getPathname(const LLView*);
template <class T> T* findChild(const std::string& name, BOOL recurse = TRUE) const
{
@@ -466,6 +468,20 @@ public:
return dynamic_cast<T*>(widgetp);
}
+ template <class T> T* getParentByType() const
+ {
+ LLView* parent = getParent();
+ while(parent)
+ {
+ if (dynamic_cast<T*>(parent))
+ {
+ return static_cast<T*>(parent);
+ }
+ parent = parent->getParent();
+ }
+ return NULL;
+ }
+
//////////////////////////////////////////////
// statics
//////////////////////////////////////////////
@@ -477,16 +493,15 @@ public:
static BOOL focusPrev(LLView::child_list_t & result);
// returns query for iterating over controls in tab order
- static const LLCtrlQuery & getTabOrderQuery();
+ static const LLViewQuery & getTabOrderQuery();
// return query for iterating over focus roots in tab order
- static const LLCtrlQuery & getFocusRootsQuery();
+ static const LLViewQuery & getFocusRootsQuery();
- static void deleteViewByHandle(LLHandle<LLView> handle);
static LLWindow* getWindow(void) { return LLUI::sWindow; }
// Set up params after XML load before calling new(),
// usually to adjust layout.
- static void applyXUILayout(Params& p, LLView* parent);
+ static void applyXUILayout(Params& p, LLView* parent, LLRect layout_rect = LLRect());
// For re-export of floaters and panels, convert the coordinate system
// to be top-left based.
@@ -510,11 +525,17 @@ public:
virtual S32 notify(const LLSD& info) { return 0;};
static const LLViewDrawContext& getDrawContext();
+
+ // Returns useful information about this ui widget.
+ LLSD getInfo(void);
protected:
void drawDebugRect();
void drawChild(LLView* childp, S32 x_offset = 0, S32 y_offset = 0, BOOL force_draw = FALSE);
void drawChildren();
+ bool visibleAndContains(S32 local_x, S32 local_Y);
+ bool visibleEnabledAndContains(S32 local_x, S32 local_y);
+ void logMouseEvent();
LLView* childrenHandleKey(KEY key, MASK mask);
LLView* childrenHandleUnicodeChar(llwchar uni_char);
@@ -537,21 +558,38 @@ protected:
LLView* childrenHandleToolTip(S32 x, S32 y, MASK mask);
ECursorType mHoverCursor;
-
+
+ virtual void addInfo(LLSD & info);
private:
+ template <typename METHOD, typename XDATA>
+ LLView* childrenHandleMouseEvent(const METHOD& method, S32 x, S32 y, XDATA extra, bool allow_mouse_block = true);
+
+ template <typename METHOD, typename CHARTYPE>
+ LLView* childrenHandleCharEvent(const std::string& desc, const METHOD& method,
+ CHARTYPE c, MASK mask);
+
+ // adapter to blur distinction between handleKey() and handleUnicodeChar()
+ // for childrenHandleCharEvent()
+ BOOL handleUnicodeCharWithDummyMask(llwchar uni_char, MASK /* dummy */, BOOL from_parent)
+ {
+ return handleUnicodeChar(uni_char, from_parent);
+ }
+
LLView* mParentView;
child_list_t mChildList;
- std::string mName;
// location in pixels, relative to surrounding structure, bottom,left=0,0
+ BOOL mVisible;
LLRect mRect;
LLRect mBoundingRect;
+
std::string mLayout;
+ std::string mName;
U32 mReshapeFlags;
- child_tab_order_t mCtrlOrder;
+ child_tab_order_t mTabOrder;
S32 mDefaultTabGroup;
S32 mLastTabGroup;
@@ -566,12 +604,9 @@ private:
BOOL mIsFocusRoot;
BOOL mUseBoundingRect; // hit test against bounding rectangle that includes all child elements
- LLRootHandle<LLView> mHandle;
BOOL mLastVisible;
- BOOL mVisible;
-
- S32 mNextInsertionOrdinal;
+ bool mInDraw;
static LLWindow* sWindow; // All root views must know about their window.
@@ -581,7 +616,35 @@ private:
LLView& getDefaultWidgetContainer() const;
+ // This allows special mouse-event targeting logic for testing.
+ typedef boost::function<bool(const LLView*, S32 x, S32 y)> DrilldownFunc;
+ static DrilldownFunc sDrilldown;
+
public:
+ // This is the only public accessor to alter sDrilldown. This is not
+ // an accident. The intended usage pattern is like:
+ // {
+ // LLView::TemporaryDrilldownFunc scoped_func(myfunctor);
+ // // ... test with myfunctor ...
+ // } // exiting block restores original LLView::sDrilldown
+ class TemporaryDrilldownFunc: public boost::noncopyable
+ {
+ public:
+ TemporaryDrilldownFunc(const DrilldownFunc& func):
+ mOldDrilldown(sDrilldown)
+ {
+ sDrilldown = func;
+ }
+
+ ~TemporaryDrilldownFunc()
+ {
+ sDrilldown = mOldDrilldown;
+ }
+
+ private:
+ DrilldownFunc mOldDrilldown;
+ };
+
// Depth in view hierarchy during rendering
static S32 sDepth;
@@ -605,17 +668,14 @@ public:
static BOOL sForceReshape;
};
-class LLCompareByTabOrder
+namespace LLInitParam
{
-public:
- 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; }
- // 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<>
+struct TypeValues<LLView::EOrientation> : public LLInitParam::TypeValuesHelper<LLView::EOrientation>
+{
+ static void declareValues();
};
+}
template <class T> T* LLView::getChild(const std::string& name, BOOL recurse) const
{
@@ -626,7 +686,7 @@ template <class T> T* LLView::getChild(const std::string& name, BOOL recurse) co
// did we find *something* with that name?
if (child)
{
- llwarns << "Found child named \"" << name << "\" but of wrong type " << typeid(*child).name() << ", expecting " << typeid(T*).name() << llendl;
+ LL_WARNS() << "Found child named \"" << name << "\" but of wrong type " << typeid(*child).name() << ", expecting " << typeid(T*).name() << LL_ENDL;
}
result = getDefaultWidget<T>(name);
if (!result)
@@ -638,11 +698,11 @@ template <class T> T* LLView::getChild(const std::string& name, BOOL recurse) co
// *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;
+ LL_WARNS() << "Making dummy " << typeid(T).name() << " named \"" << name << "\" in " << getName() << LL_ENDL;
}
else
{
- llwarns << "Failed to create dummy " << typeid(T).name() << llendl;
+ LL_WARNS() << "Failed to create dummy " << typeid(T).name() << LL_ENDL;
return NULL;
}
diff --git a/indra/llui/llviewborder.cpp b/indra/llui/llviewborder.cpp
index 89cd34c37c..919267dcc6 100644..100755
--- a/indra/llui/llviewborder.cpp
+++ b/indra/llui/llviewborder.cpp
@@ -28,6 +28,7 @@
#include "llrender.h"
#include "llfocusmgr.h"
#include "lluictrlfactory.h"
+#include "lluiimage.h"
static LLDefaultChildRegistry::Register<LLViewBorder> r("view_border");
@@ -56,9 +57,6 @@ LLViewBorder::Params::Params()
{
addSynonym(border_thickness, "thickness");
addSynonym(render_style, "style");
- name = "view_border";
- mouse_opaque = false;
- follows.flags = FOLLOWS_ALL;
}
diff --git a/indra/llui/llviewborder.h b/indra/llui/llviewborder.h
index 413ce39744..413ce39744 100644..100755
--- a/indra/llui/llviewborder.h
+++ b/indra/llui/llviewborder.h
diff --git a/indra/llui/llviewereventrecorder.cpp b/indra/llui/llviewereventrecorder.cpp
new file mode 100644
index 0000000000..9fe6a542b4
--- /dev/null
+++ b/indra/llui/llviewereventrecorder.cpp
@@ -0,0 +1,296 @@
+/**
+ * @file llviewereventrecorder.cpp
+ * @brief Viewer event recording and playback support for mouse and keyboard events
+ *
+ * $LicenseInfo:firstyear=2013&license=viewerlgpl$
+ *
+ * Copyright (c) 2013, Linden Research, Inc.
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+
+#include "llviewereventrecorder.h"
+#include "llui.h"
+#include "llleap.h"
+
+LLViewerEventRecorder::LLViewerEventRecorder() {
+
+ clear(UNDEFINED);
+ logEvents = false;
+ // Remove any previous event log file
+ std::string old_log_ui_events_to_llsd_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "SecondLife_Events_log.old");
+ LLFile::remove(old_log_ui_events_to_llsd_file);
+
+
+ mLogFilename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "SecondLife_Events_log.llsd");
+ LLFile::rename(mLogFilename, old_log_ui_events_to_llsd_file);
+
+}
+
+
+bool LLViewerEventRecorder::displayViewerEventRecorderMenuItems() {
+ return LLUI::sSettingGroups["config"]->getBOOL("ShowEventRecorderMenuItems");
+}
+
+
+void LLViewerEventRecorder::setEventLoggingOn() {
+ if (! mLog.is_open()) {
+ mLog.open(mLogFilename.c_str(), std::ios_base::out);
+ }
+ logEvents=true;
+ LL_DEBUGS() << "LLViewerEventRecorder::setEventLoggingOn event logging turned on" << LL_ENDL;
+}
+
+void LLViewerEventRecorder::setEventLoggingOff() {
+ logEvents=false;
+ mLog.flush();
+ mLog.close();
+ LL_DEBUGS() << "LLViewerEventRecorder::setEventLoggingOff event logging turned off" << LL_ENDL;
+}
+
+
+ LLViewerEventRecorder::~LLViewerEventRecorder() {
+ if (mLog.is_open()) {
+ mLog.close();
+ }
+}
+
+void LLViewerEventRecorder::clear_xui() {
+ xui.clear();
+}
+
+void LLViewerEventRecorder::clear(S32 r) {
+
+ xui.clear();
+
+ local_x=r;
+ local_y=r;
+
+ global_x=r;
+ global_y=r;
+
+
+}
+
+void LLViewerEventRecorder::setMouseLocalCoords(S32 x, S32 y) {
+ local_x=x;
+ local_y=y;
+}
+
+void LLViewerEventRecorder::setMouseGlobalCoords(S32 x, S32 y) {
+ global_x=x;
+ global_y=y;
+}
+
+void LLViewerEventRecorder::updateMouseEventInfo(S32 local_x, S32 local_y, S32 global_x, S32 global_y, std::string mName) {
+
+ LLView * target_view = LLUI::resolvePath(LLUI::getRootView(), xui);
+ if (! target_view) {
+ LL_DEBUGS() << "LLViewerEventRecorder::updateMouseEventInfo - xui path on file at moment is NOT valid - so DO NOT record these local coords" << LL_ENDL;
+ return;
+ }
+ LL_DEBUGS() << "LLViewerEventRecorder::updateMouseEventInfo b4 updatemouseeventinfo - local_x|global x "<< this->local_x << " " << this->global_x << "local/global y " << this->local_y << " " << this->global_y << " mname: " << mName << " xui: " << xui << LL_ENDL;
+
+
+ if (this->local_x < 1 && this->local_y<1 && local_x && local_y) {
+ this->local_x=local_x;
+ this->local_y=local_y;
+ }
+ this->global_x=global_x;
+ this->global_y=global_y;
+
+ // ONLY record deepest xui path for hierarchy searches - or first/only xui for floaters/panels reached via mouse captor - and llmousehandler
+ if (mName!="" && mName!="/" && xui=="") {
+ // xui=std::string("/")+mName+xui;
+ //xui=mName+xui;
+ xui = mName; // TODO review confirm we never call with partial path - also cAN REMOVE CHECK FOR "" - ON OTHER HAND IT'S PRETTY HARMLESS
+ }
+
+ LL_DEBUGS() << "LLViewerEventRecorder::updateMouseEventInfo after updatemouseeventinfo - local_x|global x "<< this->local_x << " " << this->global_x << "local/global y " << this->local_y << " " << this->global_y << " mname: " << mName << " xui: " << xui << LL_ENDL;
+}
+
+void LLViewerEventRecorder::logVisibilityChange(std::string xui, std::string name, BOOL visibility, std::string event_subtype) {
+
+ LLSD event=LLSD::emptyMap();
+
+ event.insert("event",LLSD(std::string("visibility")));
+
+ if (visibility) {
+ event.insert("visibility",LLSD(true));
+ } else {
+ event.insert("visibility",LLSD(false));
+ }
+
+ if (event_subtype!="") {
+ event.insert("event_subtype", LLSD(event_subtype));
+ }
+
+ if(name!="") {
+ event.insert("name",LLSD(name));
+ }
+
+ if (xui!="") {
+ event.insert("path",LLSD(xui));
+ }
+
+ event.insert("timestamp",LLSD(LLDate::now().asString()));
+ recordEvent(event);
+}
+
+
+std::string LLViewerEventRecorder::get_xui() {
+ return xui;
+}
+void LLViewerEventRecorder::update_xui(std::string xui) {
+ if (xui!="" && this->xui=="" ) {
+ LL_DEBUGS() << "LLViewerEventRecorder::update_xui to " << xui << LL_ENDL;
+ this->xui=xui;
+ } else {
+ LL_DEBUGS() << "LLViewerEventRecorder::update_xui called with empty string" << LL_ENDL;
+ }
+}
+
+void LLViewerEventRecorder::logKeyEvent(KEY key, MASK mask) {
+
+ // NOTE: Event recording only logs keydown events - the viewer itself hides keyup events at a fairly low level in the code and does not appear to care about them anywhere
+
+ LLSD event = LLSD::emptyMap();
+
+ event.insert("event",LLSD("type"));
+
+ // keysym ...or
+ // keycode...or
+ // char
+ event.insert("keysym",LLSD(LLKeyboard::stringFromKey(key)));
+
+ // path (optional) - for now we are not recording path for key events during record - should not be needed for full record and playback of recorded steps
+ // as a vita script - it does become useful if you edit the resulting vita script and wish to remove some steps leading to a key event - that sort of edit might
+ // break the test script and it would be useful to have more context to make these sorts of edits safer
+
+ // TODO replace this with a call which extracts to an array of names of masks (just like vita expects during playback)
+ // This is looking more and more like an object is a good idea, for this part a handy method call to setMask(mask) would be nice :-)
+ // call the func - llkeyboard::llsdStringarrayFromMask
+
+ LLSD key_mask=LLSD::emptyArray();
+
+ if (mask & MASK_CONTROL) { key_mask.append(LLSD("CTL")); } // Mac command key - has code of 0x1 in llcommon/indra_contstants
+ if (mask & MASK_ALT) { key_mask.append(LLSD("ALT")); }
+ if (mask & MASK_SHIFT) { key_mask.append(LLSD("SHIFT")); }
+ if (mask & MASK_MAC_CONTROL) { key_mask.append(LLSD("MAC_CONTROL")); }
+
+ event.insert("mask",key_mask);
+ event.insert("timestamp",LLSD(LLDate::now().asString()));
+
+ // Although vita has keyDown and keyUp requests it does not have type as a high-level concept
+ // (maybe it should) - instead it has a convenience method that generates the keydown and keyup events
+ // Here we will use "type" as our event type
+
+ LL_DEBUGS() << "LLVIewerEventRecorder::logKeyEvent Serialized LLSD for event " << event.asString() << "\n" << LL_ENDL;
+
+
+ //LL_DEBUGS() << "[VITA] key_name: " << LLKeyboard::stringFromKey(key) << "mask: "<< mask << "handled by " << getName() << LL_ENDL;
+ LL_DEBUGS() << "LLVIewerEventRecorder::logKeyEvent key_name: " << LLKeyboard::stringFromKey(key) << "mask: "<< mask << LL_ENDL;
+
+
+ recordEvent(event);
+
+}
+
+void LLViewerEventRecorder::playbackRecording() {
+
+ LLSD LeapCommand;
+
+ // ivita sets this on startup, it also sends commands to the viewer to make start, stop, and playback menu items visible in viewer
+ LeapCommand =LLUI::sSettingGroups["config"]->getLLSD("LeapPlaybackEventsCommand");
+
+ LL_DEBUGS() << "[VITA] launching playback - leap command is: " << LLSDXMLStreamer(LeapCommand) << LL_ENDL;
+ LLLeap::create("", LeapCommand, false); // exception=false
+
+}
+
+
+void LLViewerEventRecorder::recordEvent(LLSD event) {
+ LL_DEBUGS() << "LLViewerEventRecorder::recordEvent event written to log: " << LLSDXMLStreamer(event) << LL_ENDL;
+ mLog << event << std::endl;
+
+}
+void LLViewerEventRecorder::logKeyUnicodeEvent(llwchar uni_char) {
+ if (! logEvents) return;
+
+ // Note: keyUp is not captured since the viewer seems to not care about keyUp events
+
+ LLSD event=LLSD::emptyMap();
+
+ event.insert("timestamp",LLSD(LLDate::now().asString()));
+
+
+ // keysym ...or
+ // keycode...or
+ // char
+
+ LL_DEBUGS() << "Wrapped in conversion to wstring " << wstring_to_utf8str(LLWString( 1, uni_char)) << "\n" << LL_ENDL;
+
+ event.insert("char",
+ LLSD( wstring_to_utf8str(LLWString( 1,uni_char)) )
+ );
+
+ // path (optional) - for now we are not recording path for key events during record - should not be needed for full record and playback of recorded steps
+ // as a vita script - it does become useful if you edit the resulting vita script and wish to remove some steps leading to a key event - that sort of edit might
+ // break the test script and it would be useful to have more context to make these sorts of edits safer
+
+ // TODO need to consider mask keys too? Doesn't seem possible - at least not easily at this point
+
+ event.insert("event",LLSD("keyDown"));
+
+ LL_DEBUGS() << "[VITA] unicode key: " << uni_char << LL_ENDL;
+ LL_DEBUGS() << "[VITA] dumpxml " << LLSDXMLStreamer(event) << "\n" << LL_ENDL;
+
+
+ recordEvent(event);
+
+}
+
+void LLViewerEventRecorder::logMouseEvent(std::string button_state,std::string button_name)
+{
+ if (! logEvents) return;
+
+ LLSD event=LLSD::emptyMap();
+
+ event.insert("event",LLSD(std::string("mouse"+ button_state)));
+ event.insert("button",LLSD(button_name));
+ if (xui!="") {
+ event.insert("path",LLSD(xui));
+ }
+
+ if (local_x>0 && local_y>0) {
+ event.insert("local_x",LLSD(local_x));
+ event.insert("local_y",LLSD(local_y));
+ }
+
+ if (global_x>0 && global_y>0) {
+ event.insert("global_x",LLSD(global_x));
+ event.insert("global_y",LLSD(global_y));
+ }
+ event.insert("timestamp",LLSD(LLDate::now().asString()));
+ recordEvent(event);
+
+
+ clear(UNDEFINED);
+
+
+}
diff --git a/indra/llui/llviewereventrecorder.h b/indra/llui/llviewereventrecorder.h
new file mode 100644
index 0000000000..375efcc3de
--- /dev/null
+++ b/indra/llui/llviewereventrecorder.h
@@ -0,0 +1,103 @@
+/**
+ * @file llviewereventrecorder.h
+ * @brief Viewer event recording and playback support for mouse and keyboard events
+ *
+ * $LicenseInfo:firstyear=2013&license=viewerlgpl$
+ *
+ * Copyright (c) 2013, Linden Research, Inc.
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_VIEWER_EVENT_RECORDER
+#define LL_VIEWER_EVENT_RECORDER
+
+
+#include "linden_common.h"
+
+#include "lldir.h"
+#include "llsd.h"
+#include "llfile.h"
+#include "llvfile.h"
+#include "lldate.h"
+#include "llsdserialize.h"
+#include "llkeyboard.h"
+#include "llstring.h"
+
+#include <sstream>
+
+#include "llsingleton.h" // includes llerror which we need here so we can skip the include here
+
+class LLViewerEventRecorder : public LLSingleton<LLViewerEventRecorder>
+{
+
+ public:
+
+ LLViewerEventRecorder(); // TODO Protect constructor better if we can (not happy in private section) - could add a factory... - we are singleton
+ ~LLViewerEventRecorder();
+
+
+ void updateMouseEventInfo(S32 local_x,S32 local_y, S32 global_x, S32 global_y, std::string mName);
+ void setMouseLocalCoords(S32 x,S32 y);
+ void setMouseGlobalCoords(S32 x,S32 y);
+
+ void logMouseEvent(std::string button_state, std::string button_name );
+ void logKeyEvent(KEY key, MASK mask);
+ void logKeyUnicodeEvent(llwchar uni_char);
+
+ void logVisibilityChange(std::string xui, std::string name, BOOL visibility, std::string event_subtype);
+
+ void clear_xui();
+ std::string get_xui();
+ void update_xui(std::string xui);
+
+ bool getLoggingStatus(){return logEvents;};
+ void setEventLoggingOn();
+ void setEventLoggingOff();
+
+ void playbackRecording();
+
+ bool displayViewerEventRecorderMenuItems();
+
+
+ protected:
+ // On if we wish to log events at the moment - toggle via Develop/Recorder submenu
+ bool logEvents;
+
+ std::string mLogFilename;
+ llofstream mLog;
+
+
+ private:
+
+ // Mouse event info
+ S32 global_x;
+ S32 global_y;
+ S32 local_x;
+ S32 local_y;
+
+ // XUI path of UI element
+ std::string xui;
+
+ // Actually write the event out to llsd log file
+ void recordEvent(LLSD event);
+
+ void clear(S32 r);
+
+ static const S32 UNDEFINED=-1;
+};
+#endif
diff --git a/indra/llui/llviewinject.cpp b/indra/llui/llviewinject.cpp
new file mode 100755
index 0000000000..46c5839f8e
--- /dev/null
+++ b/indra/llui/llviewinject.cpp
@@ -0,0 +1,49 @@
+/**
+ * @file llviewinject.cpp
+ * @author Nat Goodspeed
+ * @date 2011-08-16
+ * @brief Implementation for llviewinject.
+ *
+ * $LicenseInfo:firstyear=2011&license=viewerlgpl$
+ * Copyright (c) 2011, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+// Precompiled header
+#include "linden_common.h"
+// associated header
+#include "llviewinject.h"
+// STL headers
+// std headers
+// external library headers
+// other Linden headers
+
+llview::TargetEvent::TargetEvent(LLView* view)
+{
+ // Walk up the view tree from target LLView to the root (NULL). If
+ // passed NULL, iterate 0 times.
+ for (; view; view = view->getParent())
+ {
+ // At each level, operator() is going to ask: for a particular parent
+ // LLView*, which of its children should I select? So for this view's
+ // parent, select this view.
+ mChildMap[view->getParent()] = view;
+ }
+}
+
+bool llview::TargetEvent::operator()(const LLView* view, S32 /*x*/, S32 /*y*/) const
+{
+ // We are being called to decide whether to direct an incoming mouse event
+ // to this child view. (Normal LLView processing is to check whether the
+ // incoming (x, y) is within the view.) Look up the parent to decide
+ // whether, for that parent, this is the previously-selected child.
+ ChildMap::const_iterator found(mChildMap.find(view->getParent()));
+ // If we're looking at a child whose parent isn't even in the map, never
+ // mind.
+ if (found == mChildMap.end())
+ {
+ return false;
+ }
+ // So, is this the predestined child for this parent?
+ return (view == found->second);
+}
diff --git a/indra/llui/llviewinject.h b/indra/llui/llviewinject.h
new file mode 100755
index 0000000000..0de3d155c4
--- /dev/null
+++ b/indra/llui/llviewinject.h
@@ -0,0 +1,56 @@
+/**
+ * @file llviewinject.h
+ * @author Nat Goodspeed
+ * @date 2011-08-16
+ * @brief Supplemental LLView functionality used for simulating UI events.
+ *
+ * $LicenseInfo:firstyear=2011&license=viewerlgpl$
+ * Copyright (c) 2011, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+#if ! defined(LL_LLVIEWINJECT_H)
+#define LL_LLVIEWINJECT_H
+
+#include "llview.h"
+#include <map>
+
+namespace llview
+{
+
+ /**
+ * TargetEvent is a callable with state, specifically intended for use as
+ * an LLView::TemporaryDrilldownFunc. Instantiate it with the desired
+ * target LLView*; pass it to a TemporaryDrilldownFunc instance;
+ * TargetEvent::operator() will then attempt to direct subsequent mouse
+ * events to the desired target LLView*. (This is an "attempt" because
+ * LLView will still balk unless the target LLView and every parent are
+ * visible and enabled.)
+ */
+ class TargetEvent
+ {
+ public:
+ /**
+ * Construct TargetEvent with the desired target LLView*. (See
+ * LLUI::resolvePath() to obtain an LLView* given a string pathname.)
+ * This sets up for operator().
+ */
+ TargetEvent(LLView* view);
+
+ /**
+ * This signature must match LLView::DrilldownFunc. When you install
+ * this TargetEvent instance using LLView::TemporaryDrilldownFunc,
+ * LLView will call this method to decide whether to propagate an
+ * incoming mouse event to the passed child LLView*.
+ */
+ bool operator()(const LLView*, S32 x, S32 y) const;
+
+ private:
+ // For a given parent LLView, identify which child to select.
+ typedef std::map<LLView*, LLView*> ChildMap;
+ ChildMap mChildMap;
+ };
+
+} // llview namespace
+
+#endif /* ! defined(LL_LLVIEWINJECT_H) */
diff --git a/indra/llui/llviewmodel.cpp b/indra/llui/llviewmodel.cpp
index a9f8acc440..282addf692 100644..100755
--- a/indra/llui/llviewmodel.cpp
+++ b/indra/llui/llviewmodel.cpp
@@ -37,13 +37,15 @@
///
LLViewModel::LLViewModel()
- : mDirty(false)
+: LLTrace::MemTrackable<LLViewModel>("LLViewModel"),
+ mDirty(false)
{
}
/// Instantiate an LLViewModel with an existing data value
LLViewModel::LLViewModel(const LLSD& value)
- : mDirty(false)
+: LLTrace::MemTrackable<LLViewModel>("LLViewModel"),
+ mDirty(false)
{
setValue(value);
}
@@ -79,8 +81,16 @@ LLTextViewModel::LLTextViewModel(const LLSD& value)
/// Update the stored value
void LLTextViewModel::setValue(const LLSD& value)
{
+ // approximate LLSD storage usage
+ disclaimMem(mDisplay.size());
LLViewModel::setValue(value);
+ disclaimMem(mDisplay);
mDisplay = utf8str_to_wstring(value.asString());
+
+ claimMem(mDisplay);
+ // approximate LLSD storage usage
+ claimMem(mDisplay.size());
+
// mDisplay and mValue agree
mUpdateFromDisplay = false;
}
@@ -91,7 +101,11 @@ void LLTextViewModel::setDisplay(const LLWString& value)
// and do the utf8str_to_wstring() to get the corresponding mDisplay
// value. But a text editor might want to edit the display string
// directly, then convert back to UTF8 on commit.
+ disclaimMem(mDisplay.size());
+ disclaimMem(mDisplay);
mDisplay = value;
+ claimMem(mDisplay);
+ claimMem(mDisplay.size());
mDirty = true;
// Don't immediately convert to UTF8 -- do it lazily -- we expect many
// more setDisplay() calls than getValue() calls. Just flag that it needs
diff --git a/indra/llui/llviewmodel.h b/indra/llui/llviewmodel.h
index 763af5d8a2..49d7c322a3 100644..100755
--- a/indra/llui/llviewmodel.h
+++ b/indra/llui/llviewmodel.h
@@ -37,8 +37,9 @@
#include "llpointer.h"
#include "llsd.h"
#include "llrefcount.h"
-#include "stdenums.h"
#include "llstring.h"
+#include "lltrace.h"
+#include "llui.h"
#include <string>
class LLScrollListItem;
@@ -60,7 +61,9 @@ typedef LLPointer<LLListViewModel> LLListViewModelPtr;
* LLViewModel data. This way, the LLViewModel is quietly deleted when the
* last referencing widget is destroyed.
*/
-class LLViewModel: public LLRefCount
+class LLViewModel
+: public LLRefCount,
+ public LLTrace::MemTrackable<LLViewModel>
{
public:
LLViewModel();
@@ -102,6 +105,7 @@ public:
// New functions
/// Get the stored value in string form
const LLWString& getDisplay() const { return mDisplay; }
+ LLWString& getEditableDisplay() { mDirty = true; mUpdateFromDisplay = true; return mDisplay; }
/**
* Set the display string directly (see LLTextEditor). What the user is
diff --git a/indra/llui/llviewquery.cpp b/indra/llui/llviewquery.cpp
index d0b78ba186..66262609ae 100644..100755
--- a/indra/llui/llviewquery.cpp
+++ b/indra/llui/llviewquery.cpp
@@ -30,7 +30,7 @@
#include "lluictrl.h"
#include "llviewquery.h"
-void LLQuerySorter::operator() (LLView * parent, viewList_t &children) const {}
+void LLQuerySorter::sort(LLView * parent, viewList_t &children) const {}
filterResult_t LLLeavesFilter::operator() (const LLView* const view, const viewList_t & children) const
{
@@ -89,8 +89,8 @@ viewList_t LLViewQuery::run(LLView* view) const
if (pre.first)
{
post = runFilters(view, filtered_children, mPostFilters);
- }
}
+ }
if(pre.first && post.first)
{
@@ -105,12 +105,12 @@ viewList_t LLViewQuery::run(LLView* view) const
return result;
}
-void LLViewQuery::filterChildren(LLView * view, viewList_t & filtered_children) const
+void LLViewQuery::filterChildren(LLView* parent_view, viewList_t & filtered_children) const
{
- LLView::child_list_t views(*(view->getChildList()));
+ LLView::child_list_t views(*(parent_view->getChildList()));
if (mSorterp)
{
- (*mSorterp)(view, views); // sort the children per the sorter
+ mSorterp->sort(parent_view, views); // sort the children per the sorter
}
for(LLView::child_list_iter_t iter = views.begin();
iter != views.end();
@@ -134,18 +134,3 @@ filterResult_t LLViewQuery::runFilters(LLView * view, const viewList_t children,
}
return result;
}
-
-class SortByTabOrder : public LLQuerySorter, public LLSingleton<SortByTabOrder>
-{
- /*virtual*/ void operator() (LLView * parent, LLView::child_list_t &children) const
- {
- children.sort(LLCompareByTabOrder(parent->getCtrlOrder()));
- }
-};
-
-LLCtrlQuery::LLCtrlQuery() :
- LLViewQuery()
-{
- setSorter(SortByTabOrder::getInstance());
-}
-
diff --git a/indra/llui/llviewquery.h b/indra/llui/llviewquery.h
index 210f95162a..9044c4ff29 100644..100755
--- a/indra/llui/llviewquery.h
+++ b/indra/llui/llviewquery.h
@@ -49,7 +49,7 @@ class LLQuerySorter
{
public:
virtual ~LLQuerySorter() {};
- virtual void operator() (LLView * parent, viewList_t &children) const;
+ virtual void sort(LLView * parent, viewList_t &children) const;
};
class LLLeavesFilter : public LLQueryFilter, public LLSingleton<LLLeavesFilter>
@@ -127,10 +127,5 @@ private:
const LLQuerySorter* mSorterp;
};
-class LLCtrlQuery : public LLViewQuery
-{
-public:
- LLCtrlQuery();
-};
#endif // LL_LLVIEWQUERY_H
diff --git a/indra/llui/llwindowshade.cpp b/indra/llui/llwindowshade.cpp
index 77e94385d4..f5c463c961 100644..100755
--- a/indra/llui/llwindowshade.cpp
+++ b/indra/llui/llwindowshade.cpp
@@ -37,18 +37,20 @@
const S32 MIN_NOTIFICATION_AREA_HEIGHT = 30;
const S32 MAX_NOTIFICATION_AREA_HEIGHT = 100;
+static LLDefaultChildRegistry::Register<LLWindowShade> r("window_shade");
+
LLWindowShade::Params::Params()
: bg_image("bg_image"),
modal("modal", false),
text_color("text_color"),
+ shade_color("shade_color"),
can_close("can_close", true)
{
- mouse_opaque = false;
+ changeDefault(mouse_opaque, false);
}
LLWindowShade::LLWindowShade(const LLWindowShade::Params& params)
: LLUICtrl(params),
- mNotification(params.notification),
mModal(params.modal),
mFormHeight(0),
mTextColor(params.text_color)
@@ -72,7 +74,7 @@ void LLWindowShade::initFromParams(const LLWindowShade::Params& params)
addChild(stackp);
LLLayoutPanel::Params panel_p;
- panel_p.rect = LLRect(0, 30, 800, 0);
+ panel_p.rect = LLRect(0, MIN_NOTIFICATION_AREA_HEIGHT, 800, 0);
panel_p.name = "notification_area";
panel_p.visible = false;
panel_p.user_resize = false;
@@ -89,7 +91,7 @@ void LLWindowShade::initFromParams(const LLWindowShade::Params& params)
panel_p.name = "background_area";
panel_p.mouse_opaque = false;
panel_p.background_visible = false;
- panel_p.bg_alpha_color = LLColor4(0.f, 0.f, 0.f, 0.2f);
+ panel_p.bg_alpha_color = params.shade_color;
LLLayoutPanel* dummy_panel = LLUICtrlFactory::create<LLLayoutPanel>(panel_p);
stackp->addChild(dummy_panel);
@@ -107,11 +109,11 @@ void LLWindowShade::initFromParams(const LLWindowShade::Params& params)
LLIconCtrl::Params icon_p;
icon_p.name = "notification_icon";
- icon_p.rect = LLRect(5, 23, 21, 8);
+ icon_p.rect = LLRect(5, 25, 21, 10);
panel->addChild(LLUICtrlFactory::create<LLIconCtrl>(icon_p));
LLTextBox::Params text_p;
- text_p.rect = LLRect(31, 20, panel->getRect().getWidth() - 5, 0);
+ text_p.rect = LLRect(31, 23, panel->getRect().getWidth() - 5, 3);
text_p.follows.flags = FOLLOWS_ALL;
text_p.text_color = mTextColor;
text_p.font = LLFontGL::getFontSansSerifSmall();
@@ -125,41 +127,132 @@ void LLWindowShade::initFromParams(const LLWindowShade::Params& params)
panel_p.auto_resize = false;
panel_p.user_resize = false;
panel_p.name="form_elements";
- panel_p.rect = LLRect(0, 30, 130, 0);
+ panel_p.rect = LLRect(0, MIN_NOTIFICATION_AREA_HEIGHT, 130, 0);
LLLayoutPanel* form_elements_panel = LLUICtrlFactory::create<LLLayoutPanel>(panel_p);
stackp->addChild(form_elements_panel);
- if (params.can_close)
+ panel_p = LLUICtrlFactory::getDefaultParams<LLLayoutPanel>();
+ panel_p.auto_resize = false;
+ panel_p.user_resize = false;
+ panel_p.rect = LLRect(0, MIN_NOTIFICATION_AREA_HEIGHT, 25, 0);
+ panel_p.name = "close_panel";
+ LLLayoutPanel* close_panel = LLUICtrlFactory::create<LLLayoutPanel>(panel_p);
+ stackp->addChild(close_panel);
+
+ LLButton::Params button_p;
+ button_p.name = "close_notification";
+ button_p.rect = LLRect(5, 23, 21, 7);
+ button_p.image_color.control="DkGray_66";
+ button_p.image_unselected.name="Icon_Close_Foreground";
+ button_p.image_selected.name="Icon_Close_Press";
+ button_p.click_callback.function = boost::bind(&LLWindowShade::onCloseNotification, this);
+
+ close_panel->addChild(LLUICtrlFactory::create<LLButton>(button_p));
+
+ close_panel->setVisible(params.can_close);
+}
+
+void LLWindowShade::draw()
+{
+ LLRect message_rect = getChild<LLTextBox>("notification_text")->getTextBoundingRect();
+
+ LLLayoutPanel* notification_area = getChild<LLLayoutPanel>("notification_area");
+
+ notification_area->reshape(notification_area->getRect().getWidth(),
+ llclamp(message_rect.getHeight() + 15,
+ llmax(mFormHeight, MIN_NOTIFICATION_AREA_HEIGHT),
+ MAX_NOTIFICATION_AREA_HEIGHT));
+
+ LLUICtrl::draw();
+
+ while(!mNotifications.empty() && !mNotifications.back()->isActive())
+ {
+ mNotifications.pop_back();
+ // go ahead and hide
+ hide();
+ }
+
+ if (mNotifications.empty())
+ {
+ hide();
+ }
+ else if (notification_area->getVisibleAmount() < 0.01f)
+ {
+ displayLatestNotification();
+ }
+
+ if (!notification_area->getVisible() && (notification_area->getVisibleAmount() < 0.001f))
{
- panel_p = LLUICtrlFactory::getDefaultParams<LLLayoutPanel>();
- panel_p.auto_resize = false;
- panel_p.user_resize = false;
- panel_p.rect = LLRect(0, 30, 25, 0);
- LLLayoutPanel* close_panel = LLUICtrlFactory::create<LLLayoutPanel>(panel_p);
- stackp->addChild(close_panel);
-
- LLButton::Params button_p;
- button_p.name = "close_notification";
- button_p.rect = LLRect(5, 23, 21, 7);
- button_p.image_color.control="DkGray_66";
- button_p.image_unselected.name="Icon_Close_Foreground";
- button_p.image_selected.name="Icon_Close_Press";
- button_p.click_callback.function = boost::bind(&LLWindowShade::onCloseNotification, this);
-
- close_panel->addChild(LLUICtrlFactory::create<LLButton>(button_p));
+ getChildRef<LLLayoutPanel>("background_area").setBackgroundVisible(false);
+ setMouseOpaque(false);
}
+}
+
+void LLWindowShade::hide()
+{
+ getChildRef<LLLayoutPanel>("notification_area").setVisible(false);
+}
+
+void LLWindowShade::onCloseNotification()
+{
+ if (!mNotifications.empty())
+ LLNotifications::instance().cancel(mNotifications.back());
+}
+
+void LLWindowShade::onClickIgnore(LLUICtrl* ctrl)
+{
+ LLNotificationPtr notify = getCurrentNotification();
+ if (!notify) return;
+
+ bool check = ctrl->getValue().asBoolean();
+ if (notify->getForm()->getIgnoreType() == LLNotificationForm::IGNORE_SHOW_AGAIN)
+ {
+ // question was "show again" so invert value to get "ignore"
+ check = !check;
+ }
+ notify->setIgnored(check);
+}
+
+void LLWindowShade::onClickNotificationButton(const std::string& name)
+{
+ LLNotificationPtr notify = getCurrentNotification();
+ if (!notify) return;
- LLSD payload = mNotification->getPayload();
+ mNotificationResponse[name] = true;
+
+ notify->respond(mNotificationResponse);
+}
- LLNotificationFormPtr formp = mNotification->getForm();
+void LLWindowShade::onEnterNotificationText(LLUICtrl* ctrl, const std::string& name)
+{
+ mNotificationResponse[name] = ctrl->getValue().asString();
+}
+
+void LLWindowShade::show(LLNotificationPtr notification)
+{
+ mNotifications.push_back(notification);
+
+ displayLatestNotification();
+}
+
+void LLWindowShade::displayLatestNotification()
+{
+ if (mNotifications.empty()) return;
+
+ LLNotificationPtr notification = mNotifications.back();
+
+ LLSD payload = notification->getPayload();
+
+ LLNotificationFormPtr formp = notification->getForm();
LLLayoutPanel& notification_area = getChildRef<LLLayoutPanel>("notification_area");
- notification_area.getChild<LLUICtrl>("notification_icon")->setValue(mNotification->getIcon());
- notification_area.getChild<LLUICtrl>("notification_text")->setValue(mNotification->getMessage());
- notification_area.getChild<LLUICtrl>("notification_text")->setToolTip(mNotification->getMessage());
+ notification_area.getChild<LLUICtrl>("notification_icon")->setValue(notification->getIcon());
+ notification_area.getChild<LLUICtrl>("notification_text")->setValue(notification->getMessage());
+ notification_area.getChild<LLUICtrl>("notification_text")->setToolTip(notification->getMessage());
LLNotificationForm::EIgnoreType ignore_type = formp->getIgnoreType();
LLLayoutPanel& form_elements = notification_area.getChildRef<LLLayoutPanel>("form_elements");
form_elements.deleteAllChildren();
+ form_elements.reshape(form_elements.getRect().getWidth(), MIN_NOTIFICATION_AREA_HEIGHT);
const S32 FORM_PADDING_HORIZONTAL = 10;
const S32 FORM_PADDING_VERTICAL = 3;
@@ -229,7 +322,7 @@ void LLWindowShade::initFromParams(const LLWindowShade::Params& params)
label_p.v_pad = 5;
LLTextBox* textbox = LLUICtrlFactory::create<LLTextBox>(label_p);
textbox->reshapeToFitText();
- textbox->reshape(textbox->getRect().getWidth(), form_elements.getRect().getHeight() - 2 * FORM_PADDING_VERTICAL);
+ textbox->reshape(textbox->getRect().getWidth(), MIN_NOTIFICATION_AREA_HEIGHT - 2 * FORM_PADDING_VERTICAL);
form_elements.addChild(textbox);
cur_x = textbox->getRect().mRight + FORM_PADDING_HORIZONTAL;
@@ -249,7 +342,7 @@ void LLWindowShade::initFromParams(const LLWindowShade::Params& params)
}
}
- mFormHeight = form_elements.getRect().getHeight() - (cur_y - FORM_PADDING_VERTICAL) + WIDGET_HEIGHT;
+ mFormHeight = form_elements.getRect().getHeight() - (cur_y - WIDGET_HEIGHT - FORM_PADDING_VERTICAL);
form_elements.reshape(form_width, mFormHeight);
form_elements.setMinDim(form_width);
@@ -261,68 +354,39 @@ void LLWindowShade::initFromParams(const LLWindowShade::Params& params)
{
(*it)->translate(0, delta_y);
}
-}
-void LLWindowShade::show()
-{
getChildRef<LLLayoutPanel>("notification_area").setVisible(true);
getChildRef<LLLayoutPanel>("background_area").setBackgroundVisible(mModal);
setMouseOpaque(mModal);
}
-void LLWindowShade::draw()
+void LLWindowShade::setBackgroundImage(LLUIImage* image)
{
- LLRect message_rect = getChild<LLTextBox>("notification_text")->getTextBoundingRect();
-
- LLLayoutPanel* notification_area = getChild<LLLayoutPanel>("notification_area");
-
- notification_area->reshape(notification_area->getRect().getWidth(),
- llclamp(message_rect.getHeight() + 10,
- llmin(mFormHeight, MAX_NOTIFICATION_AREA_HEIGHT),
- MAX_NOTIFICATION_AREA_HEIGHT));
-
- LLUICtrl::draw();
- if (mNotification && !mNotification->isActive())
- {
- hide();
- }
+ getChild<LLLayoutPanel>("notification_area")->setTransparentImage(image);
}
-void LLWindowShade::hide()
+void LLWindowShade::setTextColor(LLColor4 color)
{
- getChildRef<LLLayoutPanel>("notification_area").setVisible(false);
- getChildRef<LLLayoutPanel>("background_area").setBackgroundVisible(false);
-
- setMouseOpaque(false);
+ getChild<LLTextBox>("notification_text")->setColor(color);
}
-void LLWindowShade::onCloseNotification()
+bool LLWindowShade::isShown() const
{
- LLNotifications::instance().cancel(mNotification);
+ return getChildRef<LLLayoutPanel>("notification_area").getVisible();
}
-void LLWindowShade::onClickIgnore(LLUICtrl* ctrl)
+void LLWindowShade::setCanClose(bool can_close)
{
- bool check = ctrl->getValue().asBoolean();
- if (mNotification && mNotification->getForm()->getIgnoreType() == LLNotificationForm::IGNORE_SHOW_AGAIN)
- {
- // question was "show again" so invert value to get "ignore"
- check = !check;
- }
- mNotification->setIgnored(check);
+ getChildView("close_panel")->setVisible(can_close);
}
-void LLWindowShade::onClickNotificationButton(const std::string& name)
+LLNotificationPtr LLWindowShade::getCurrentNotification()
{
- if (!mNotification) return;
-
- mNotificationResponse[name] = true;
-
- mNotification->respond(mNotificationResponse);
+ if (mNotifications.empty())
+ {
+ return LLNotificationPtr();
+ }
+ return mNotifications.back();
}
-void LLWindowShade::onEnterNotificationText(LLUICtrl* ctrl, const std::string& name)
-{
- mNotificationResponse[name] = ctrl->getValue().asString();
-}
diff --git a/indra/llui/llwindowshade.h b/indra/llui/llwindowshade.h
index 0047195929..6d753d1161 100644..100755
--- a/indra/llui/llwindowshade.h
+++ b/indra/llui/llwindowshade.h
@@ -29,26 +29,35 @@
#include "lluictrl.h"
#include "llnotifications.h"
+#include "lluiimage.h"
class LLWindowShade : public LLUICtrl
{
public:
struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
{
- Mandatory<LLNotificationPtr> notification;
Optional<LLUIImage*> bg_image;
- Optional<LLUIColor> text_color;
+ Optional<LLUIColor> text_color,
+ shade_color;
Optional<bool> modal,
can_close;
Params();
};
- void show();
+ void show(LLNotificationPtr);
/*virtual*/ void draw();
void hide();
+
+ bool isShown() const;
+
+ void setBackgroundImage(LLUIImage* image);
+ void setTextColor(LLColor4 color);
+ void setCanClose(bool can_close);
private:
+ void displayLatestNotification();
+ LLNotificationPtr getCurrentNotification();
friend class LLUICtrlFactory;
LLWindowShade(const Params& p);
@@ -59,7 +68,7 @@ private:
void onEnterNotificationText(LLUICtrl* ctrl, const std::string& name);
void onClickIgnore(LLUICtrl* ctrl);
- LLNotificationPtr mNotification;
+ std::vector<LLNotificationPtr> mNotifications;
LLSD mNotificationResponse;
bool mModal;
S32 mFormHeight;
diff --git a/indra/llui/llxuiparser.cpp b/indra/llui/llxuiparser.cpp
new file mode 100755
index 0000000000..99a0869ce3
--- /dev/null
+++ b/indra/llui/llxuiparser.cpp
@@ -0,0 +1,1785 @@
+/**
+ * @file llxuiparser.cpp
+ * @brief Utility functions for handling XUI structures in XML
+ *
+ * $LicenseInfo:firstyear=2003&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llxuiparser.h"
+
+#include "llxmlnode.h"
+#include "llfasttimer.h"
+#ifdef LL_USESYSTEMLIBS
+#include <expat.h>
+#else
+#include "expat/expat.h"
+#endif
+
+#include <fstream>
+#include <boost/tokenizer.hpp>
+#include <boost/bind.hpp>
+//#include <boost/spirit/include/qi.hpp>
+#include <boost/spirit/include/classic_core.hpp>
+
+#include "lluicolor.h"
+#include "v3math.h"
+using namespace BOOST_SPIRIT_CLASSIC_NS;
+
+const S32 MAX_STRING_ATTRIBUTE_SIZE = 40;
+
+static LLInitParam::Parser::parser_read_func_map_t sXSDReadFuncs;
+static LLInitParam::Parser::parser_write_func_map_t sXSDWriteFuncs;
+static LLInitParam::Parser::parser_inspect_func_map_t sXSDInspectFuncs;
+
+static LLInitParam::Parser::parser_read_func_map_t sSimpleXUIReadFuncs;
+static LLInitParam::Parser::parser_write_func_map_t sSimpleXUIWriteFuncs;
+static LLInitParam::Parser::parser_inspect_func_map_t sSimpleXUIInspectFuncs;
+
+const char* NO_VALUE_MARKER = "no_value";
+
+#ifdef LL_WINDOWS
+const S32 LINE_NUMBER_HERE = 0;
+#endif
+
+struct MaxOccursValues : public LLInitParam::TypeValuesHelper<U32, MaxOccursValues>
+{
+ static void declareValues()
+ {
+ declare("unbounded", U32_MAX);
+ }
+};
+
+struct Occurs : public LLInitParam::Block<Occurs>
+{
+ Optional<U32> minOccurs;
+ Optional<U32, MaxOccursValues> maxOccurs;
+
+ Occurs()
+ : minOccurs("minOccurs", 0),
+ maxOccurs("maxOccurs", U32_MAX)
+
+ {}
+};
+
+typedef enum
+{
+ USE_REQUIRED,
+ USE_OPTIONAL
+} EUse;
+
+namespace LLInitParam
+{
+ template<>
+ struct TypeValues<EUse> : public TypeValuesHelper<EUse>
+ {
+ static void declareValues()
+ {
+ declare("required", USE_REQUIRED);
+ declare("optional", USE_OPTIONAL);
+ }
+ };
+}
+
+struct Element;
+struct Group;
+struct Sequence;
+
+struct All : public LLInitParam::Block<All, Occurs>
+{
+ Multiple< Lazy<Element, IS_A_BLOCK> > elements;
+
+ All()
+ : elements("element")
+ {
+ maxOccurs = 1;
+ }
+};
+
+struct Attribute : public LLInitParam::Block<Attribute>
+{
+ Mandatory<std::string> name,
+ type;
+ Mandatory<EUse> use;
+
+ Attribute()
+ : name("name"),
+ type("type"),
+ use("use")
+ {}
+};
+
+struct Any : public LLInitParam::Block<Any, Occurs>
+{
+ Optional<std::string> _namespace;
+
+ Any()
+ : _namespace("namespace")
+ {}
+};
+
+struct Choice : public LLInitParam::ChoiceBlock<Choice, Occurs>
+{
+ Alternative< Lazy<Element, IS_A_BLOCK> > element;
+ Alternative< Lazy<Group, IS_A_BLOCK> > group;
+ Alternative< Lazy<Choice, IS_A_BLOCK> > choice;
+ Alternative< Lazy<Sequence, IS_A_BLOCK> > sequence;
+ Alternative< Lazy<Any> > any;
+
+ Choice()
+ : element("element"),
+ group("group"),
+ choice("choice"),
+ sequence("sequence"),
+ any("any")
+ {}
+
+};
+
+struct Sequence : public LLInitParam::ChoiceBlock<Sequence, Occurs>
+{
+ Alternative< Lazy<Element, IS_A_BLOCK> > element;
+ Alternative< Lazy<Group, IS_A_BLOCK> > group;
+ Alternative< Lazy<Choice> > choice;
+ Alternative< Lazy<Sequence, IS_A_BLOCK> > sequence;
+ Alternative< Lazy<Any> > any;
+};
+
+struct GroupContents : public LLInitParam::ChoiceBlock<GroupContents, Occurs>
+{
+ Alternative<All> all;
+ Alternative<Choice> choice;
+ Alternative<Sequence> sequence;
+
+ GroupContents()
+ : all("all"),
+ choice("choice"),
+ sequence("sequence")
+ {}
+};
+
+struct Group : public LLInitParam::Block<Group, GroupContents>
+{
+ Optional<std::string> name,
+ ref;
+
+ Group()
+ : name("name"),
+ ref("ref")
+ {}
+};
+
+struct Restriction : public LLInitParam::Block<Restriction>
+{
+};
+
+struct Extension : public LLInitParam::Block<Extension>
+{
+};
+
+struct SimpleContent : public LLInitParam::ChoiceBlock<SimpleContent>
+{
+ Alternative<Restriction> restriction;
+ Alternative<Extension> extension;
+
+ SimpleContent()
+ : restriction("restriction"),
+ extension("extension")
+ {}
+};
+
+struct SimpleType : public LLInitParam::Block<SimpleType>
+{
+ // TODO
+};
+
+struct ComplexContent : public LLInitParam::Block<ComplexContent, SimpleContent>
+{
+ Optional<bool> mixed;
+
+ ComplexContent()
+ : mixed("mixed", true)
+ {}
+};
+
+struct ComplexTypeContents : public LLInitParam::ChoiceBlock<ComplexTypeContents>
+{
+ Alternative<SimpleContent> simple_content;
+ Alternative<ComplexContent> complex_content;
+ Alternative<Group> group;
+ Alternative<All> all;
+ Alternative<Choice> choice;
+ Alternative<Sequence> sequence;
+
+ ComplexTypeContents()
+ : simple_content("simpleContent"),
+ complex_content("complexContent"),
+ group("group"),
+ all("all"),
+ choice("choice"),
+ sequence("sequence")
+ {}
+};
+
+struct ComplexType : public LLInitParam::Block<ComplexType, ComplexTypeContents>
+{
+ Optional<std::string> name;
+ Optional<bool> mixed;
+
+ Multiple<Attribute> attribute;
+ Multiple< Lazy<Element, IS_A_BLOCK > > elements;
+
+ ComplexType()
+ : name("name"),
+ attribute("xs:attribute"),
+ elements("xs:element"),
+ mixed("mixed")
+ {
+ }
+};
+
+struct ElementContents : public LLInitParam::ChoiceBlock<ElementContents, Occurs>
+{
+ Alternative<SimpleType> simpleType;
+ Alternative<ComplexType> complexType;
+
+ ElementContents()
+ : simpleType("simpleType"),
+ complexType("complexType")
+ {}
+};
+
+struct Element : public LLInitParam::Block<Element, ElementContents>
+{
+ Optional<std::string> name,
+ ref,
+ type;
+
+ Element()
+ : name("xs:name"),
+ ref("xs:ref"),
+ type("xs:type")
+ {}
+};
+
+struct Schema : public LLInitParam::Block<Schema>
+{
+private:
+ Mandatory<std::string> targetNamespace,
+ xmlns,
+ xs;
+
+public:
+ Optional<std::string> attributeFormDefault,
+ elementFormDefault;
+
+ Mandatory<Element> root_element;
+
+ void setNameSpace(const std::string& ns) {targetNamespace = ns; xmlns = ns;}
+
+ Schema(const std::string& ns = LLStringUtil::null)
+ : attributeFormDefault("attributeFormDefault"),
+ elementFormDefault("elementFormDefault"),
+ xs("xmlns:xs"),
+ targetNamespace("targetNamespace"),
+ xmlns("xmlns"),
+ root_element("xs:element")
+ {
+ attributeFormDefault = "unqualified";
+ elementFormDefault = "qualified";
+ xs = "http://www.w3.org/2001/XMLSchema";
+ if (!ns.empty())
+ {
+ setNameSpace(ns);
+ };
+ }
+};
+
+//
+// LLXSDWriter
+//
+LLXSDWriter::LLXSDWriter()
+: Parser(sXSDReadFuncs, sXSDWriteFuncs, sXSDInspectFuncs)
+{
+ registerInspectFunc<bool>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:boolean", _1, _2, _3, _4));
+ registerInspectFunc<std::string>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:string", _1, _2, _3, _4));
+ registerInspectFunc<U8>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:unsignedByte", _1, _2, _3, _4));
+ registerInspectFunc<S8>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:signedByte", _1, _2, _3, _4));
+ registerInspectFunc<U16>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:unsignedShort", _1, _2, _3, _4));
+ registerInspectFunc<S16>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:signedShort", _1, _2, _3, _4));
+ registerInspectFunc<U32>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:unsignedInt", _1, _2, _3, _4));
+ registerInspectFunc<S32>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:integer", _1, _2, _3, _4));
+ registerInspectFunc<F32>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:float", _1, _2, _3, _4));
+ registerInspectFunc<F64>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:double", _1, _2, _3, _4));
+ registerInspectFunc<LLColor4>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:string", _1, _2, _3, _4));
+ registerInspectFunc<LLUIColor>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:string", _1, _2, _3, _4));
+ registerInspectFunc<LLUUID>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:string", _1, _2, _3, _4));
+ registerInspectFunc<LLSD>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:string", _1, _2, _3, _4));
+}
+
+LLXSDWriter::~LLXSDWriter() {}
+
+void LLXSDWriter::writeXSD(const std::string& type_name, LLXMLNodePtr node, const LLInitParam::BaseBlock& block, const std::string& xml_namespace)
+{
+ Schema schema(xml_namespace);
+
+ schema.root_element.name = type_name;
+ Choice& choice = schema.root_element.complexType.choice;
+
+ choice.minOccurs = 0;
+ choice.maxOccurs = "unbounded";
+
+ mSchemaNode = node;
+ //node->setName("xs:schema");
+ //node->createChild("attributeFormDefault", true)->setStringValue("unqualified");
+ //node->createChild("elementFormDefault", true)->setStringValue("qualified");
+ //node->createChild("targetNamespace", true)->setStringValue(xml_namespace);
+ //node->createChild("xmlns:xs", true)->setStringValue("http://www.w3.org/2001/XMLSchema");
+ //node->createChild("xmlns", true)->setStringValue(xml_namespace);
+
+ //node = node->createChild("xs:complexType", false);
+ //node->createChild("name", true)->setStringValue(type_name);
+ //node->createChild("mixed", true)->setStringValue("true");
+
+ //mAttributeNode = node;
+ //mElementNode = node->createChild("xs:choice", false);
+ //mElementNode->createChild("minOccurs", true)->setStringValue("0");
+ //mElementNode->createChild("maxOccurs", true)->setStringValue("unbounded");
+ block.inspectBlock(*this);
+
+ // duplicate element choices
+ LLXMLNodeList children;
+ mElementNode->getChildren("xs:element", children, FALSE);
+ for (LLXMLNodeList::iterator child_it = children.begin(); child_it != children.end(); ++child_it)
+ {
+ LLXMLNodePtr child_copy = child_it->second->deepCopy();
+ std::string child_name;
+ child_copy->getAttributeString("name", child_name);
+ child_copy->setAttributeString("name", type_name + "." + child_name);
+ mElementNode->addChild(child_copy);
+ }
+
+ LLXMLNodePtr element_declaration_node = mSchemaNode->createChild("xs:element", false);
+ element_declaration_node->createChild("name", true)->setStringValue(type_name);
+ element_declaration_node->createChild("type", true)->setStringValue(type_name);
+}
+
+void LLXSDWriter::writeAttribute(const std::string& type, const Parser::name_stack_t& stack, S32 min_count, S32 max_count, const std::vector<std::string>* possible_values)
+{
+ name_stack_t non_empty_names;
+ std::string attribute_name;
+ for (name_stack_t::const_iterator it = stack.begin();
+ it != stack.end();
+ ++it)
+ {
+ const std::string& name = it->first;
+ if (!name.empty())
+ {
+ non_empty_names.push_back(*it);
+ }
+ }
+
+ for (name_stack_t::const_iterator it = non_empty_names.begin();
+ it != non_empty_names.end();
+ ++it)
+ {
+ if (!attribute_name.empty())
+ {
+ attribute_name += ".";
+ }
+ attribute_name += it->first;
+ }
+
+ // only flag non-nested attributes as mandatory, nested attributes have variant syntax
+ // that can't be properly constrained in XSD
+ // e.g. <foo mandatory.value="bar"/> vs <foo><mandatory value="bar"/></foo>
+ bool attribute_mandatory = min_count == 1 && max_count == 1 && non_empty_names.size() == 1;
+
+ // don't bother supporting "Multiple" params as xml attributes
+ if (max_count <= 1)
+ {
+ // add compound attribute to root node
+ addAttributeToSchema(mAttributeNode, attribute_name, type, attribute_mandatory, possible_values);
+ }
+
+ // now generated nested elements for compound attributes
+ if (non_empty_names.size() > 1 && !attribute_mandatory)
+ {
+ std::string element_name;
+
+ // traverse all but last element, leaving that as an attribute name
+ name_stack_t::const_iterator end_it = non_empty_names.end();
+ end_it--;
+
+ for (name_stack_t::const_iterator it = non_empty_names.begin();
+ it != end_it;
+ ++it)
+ {
+ if (it != non_empty_names.begin())
+ {
+ element_name += ".";
+ }
+ element_name += it->first;
+ }
+
+ std::string short_attribute_name = non_empty_names.back().first;
+
+ LLXMLNodePtr complex_type_node;
+
+ // find existing element node here, starting at tail of child list
+ if (mElementNode->mChildren.notNull())
+ {
+ for(LLXMLNodePtr element = mElementNode->mChildren->tail;
+ element.notNull();
+ element = element->mPrev)
+ {
+ std::string name;
+ if(element->getAttributeString("name", name) && name == element_name)
+ {
+ complex_type_node = element->mChildren->head;
+ break;
+ }
+ }
+ }
+ //create complex_type node
+ //
+ //<xs:element
+ // maxOccurs="1"
+ // minOccurs="0"
+ // name="name">
+ // <xs:complexType>
+ // </xs:complexType>
+ //</xs:element>
+ if(complex_type_node.isNull())
+ {
+ complex_type_node = mElementNode->createChild("xs:element", false);
+
+ complex_type_node->createChild("minOccurs", true)->setIntValue(min_count);
+ complex_type_node->createChild("maxOccurs", true)->setIntValue(max_count);
+ complex_type_node->createChild("name", true)->setStringValue(element_name);
+ complex_type_node = complex_type_node->createChild("xs:complexType", false);
+ }
+
+ addAttributeToSchema(complex_type_node, short_attribute_name, type, false, possible_values);
+ }
+}
+
+void LLXSDWriter::addAttributeToSchema(LLXMLNodePtr type_declaration_node, const std::string& attribute_name, const std::string& type, bool mandatory, const std::vector<std::string>* possible_values)
+{
+ if (!attribute_name.empty())
+ {
+ LLXMLNodePtr new_enum_type_node;
+ if (possible_values != NULL)
+ {
+ // custom attribute type, for example
+ //<xs:simpleType>
+ // <xs:restriction
+ // base="xs:string">
+ // <xs:enumeration
+ // value="a" />
+ // <xs:enumeration
+ // value="b" />
+ // </xs:restriction>
+ // </xs:simpleType>
+ new_enum_type_node = new LLXMLNode("xs:simpleType", false);
+
+ LLXMLNodePtr restriction_node = new_enum_type_node->createChild("xs:restriction", false);
+ restriction_node->createChild("base", true)->setStringValue("xs:string");
+
+ for (std::vector<std::string>::const_iterator it = possible_values->begin();
+ it != possible_values->end();
+ ++it)
+ {
+ LLXMLNodePtr enum_node = restriction_node->createChild("xs:enumeration", false);
+ enum_node->createChild("value", true)->setStringValue(*it);
+ }
+ }
+
+ string_set_t& attributes_written = mAttributesWritten[type_declaration_node];
+
+ string_set_t::iterator found_it = attributes_written.lower_bound(attribute_name);
+
+ // attribute not yet declared
+ if (found_it == attributes_written.end() || attributes_written.key_comp()(attribute_name, *found_it))
+ {
+ attributes_written.insert(found_it, attribute_name);
+
+ LLXMLNodePtr attribute_node = type_declaration_node->createChild("xs:attribute", false);
+
+ // attribute name
+ attribute_node->createChild("name", true)->setStringValue(attribute_name);
+
+ if (new_enum_type_node.notNull())
+ {
+ attribute_node->addChild(new_enum_type_node);
+ }
+ else
+ {
+ // simple attribute type
+ attribute_node->createChild("type", true)->setStringValue(type);
+ }
+
+ // required or optional
+ attribute_node->createChild("use", true)->setStringValue(mandatory ? "required" : "optional");
+ }
+ // attribute exists...handle collision of same name attributes with potentially different types
+ else
+ {
+ LLXMLNodePtr attribute_declaration;
+ if (type_declaration_node.notNull())
+ {
+ for(LLXMLNodePtr node = type_declaration_node->mChildren->tail;
+ node.notNull();
+ node = node->mPrev)
+ {
+ std::string name;
+ if (node->getAttributeString("name", name) && name == attribute_name)
+ {
+ attribute_declaration = node;
+ break;
+ }
+ }
+ }
+
+ bool new_type_is_enum = new_enum_type_node.notNull();
+ bool existing_type_is_enum = !attribute_declaration->hasAttribute("type");
+
+ // either type is enum, revert to string in collision
+ // don't bother to check for enum equivalence
+ if (new_type_is_enum || existing_type_is_enum)
+ {
+ if (attribute_declaration->hasAttribute("type"))
+ {
+ attribute_declaration->setAttributeString("type", "xs:string");
+ }
+ else
+ {
+ attribute_declaration->createChild("type", true)->setStringValue("xs:string");
+ }
+ attribute_declaration->deleteChildren("xs:simpleType");
+ }
+ else
+ {
+ // check for collision of different standard types
+ std::string existing_type;
+ attribute_declaration->getAttributeString("type", existing_type);
+ // if current type is not the same as the new type, revert to strnig
+ if (existing_type != type)
+ {
+ // ...than use most general type, string
+ attribute_declaration->setAttributeString("type", "string");
+ }
+ }
+ }
+ }
+}
+
+//
+// LLXUIXSDWriter
+//
+void LLXUIXSDWriter::writeXSD(const std::string& type_name, const std::string& path, const LLInitParam::BaseBlock& block)
+{
+ std::string file_name(path);
+ file_name += type_name + ".xsd";
+ LLXMLNodePtr root_nodep = new LLXMLNode();
+
+ LLXSDWriter::writeXSD(type_name, root_nodep, block, "http://www.lindenlab.com/xui");
+
+ // add includes for all possible children
+ const std::type_info* type = *LLWidgetTypeRegistry::instance().getValue(type_name);
+ const widget_registry_t* widget_registryp = LLChildRegistryRegistry::instance().getValue(type);
+
+ // add choices for valid children
+ if (widget_registryp)
+ {
+ // add include declarations for all valid children
+ for (widget_registry_t::Registrar::registry_map_t::const_iterator it = widget_registryp->currentRegistrar().beginItems();
+ it != widget_registryp->currentRegistrar().endItems();
+ ++it)
+ {
+ std::string widget_name = it->first;
+ if (widget_name == type_name)
+ {
+ continue;
+ }
+ LLXMLNodePtr nodep = new LLXMLNode("xs:include", false);
+ nodep->createChild("schemaLocation", true)->setStringValue(widget_name + ".xsd");
+
+ // add to front of schema
+ mSchemaNode->addChild(nodep);
+ }
+
+ for (widget_registry_t::Registrar::registry_map_t::const_iterator it = widget_registryp->currentRegistrar().beginItems();
+ it != widget_registryp->currentRegistrar().endItems();
+ ++it)
+ {
+ std::string widget_name = it->first;
+ //<xs:element name="widget_name" type="widget_name">
+ LLXMLNodePtr widget_node = mElementNode->createChild("xs:element", false);
+ widget_node->createChild("name", true)->setStringValue(widget_name);
+ widget_node->createChild("type", true)->setStringValue(widget_name);
+ }
+ }
+
+ LLFILE* xsd_file = LLFile::fopen(file_name.c_str(), "w");
+ LLXMLNode::writeHeaderToFile(xsd_file);
+ root_nodep->writeToFile(xsd_file);
+ fclose(xsd_file);
+}
+
+static LLInitParam::Parser::parser_read_func_map_t sXUIReadFuncs;
+static LLInitParam::Parser::parser_write_func_map_t sXUIWriteFuncs;
+static LLInitParam::Parser::parser_inspect_func_map_t sXUIInspectFuncs;
+
+//
+// LLXUIParser
+//
+LLXUIParser::LLXUIParser()
+: Parser(sXUIReadFuncs, sXUIWriteFuncs, sXUIInspectFuncs),
+ mCurReadDepth(0)
+{
+ if (sXUIReadFuncs.empty())
+ {
+ registerParserFuncs<LLInitParam::Flag>(readFlag, writeFlag);
+ registerParserFuncs<bool>(readBoolValue, writeBoolValue);
+ registerParserFuncs<std::string>(readStringValue, writeStringValue);
+ registerParserFuncs<U8>(readU8Value, writeU8Value);
+ registerParserFuncs<S8>(readS8Value, writeS8Value);
+ registerParserFuncs<U16>(readU16Value, writeU16Value);
+ registerParserFuncs<S16>(readS16Value, writeS16Value);
+ registerParserFuncs<U32>(readU32Value, writeU32Value);
+ registerParserFuncs<S32>(readS32Value, writeS32Value);
+ registerParserFuncs<F32>(readF32Value, writeF32Value);
+ registerParserFuncs<F64>(readF64Value, writeF64Value);
+ registerParserFuncs<LLVector3>(readVector3Value, writeVector3Value);
+ registerParserFuncs<LLColor4>(readColor4Value, writeColor4Value);
+ registerParserFuncs<LLUIColor>(readUIColorValue, writeUIColorValue);
+ registerParserFuncs<LLUUID>(readUUIDValue, writeUUIDValue);
+ registerParserFuncs<LLSD>(readSDValue, writeSDValue);
+ }
+}
+
+static LLTrace::BlockTimerStatHandle FTM_PARSE_XUI("XUI Parsing");
+const LLXMLNodePtr DUMMY_NODE = new LLXMLNode();
+
+void LLXUIParser::readXUI(LLXMLNodePtr node, LLInitParam::BaseBlock& block, const std::string& filename, bool silent)
+{
+ LL_RECORD_BLOCK_TIME(FTM_PARSE_XUI);
+ mNameStack.clear();
+ mRootNodeName = node->getName()->mString;
+ mCurFileName = filename;
+ mCurReadDepth = 0;
+ setParseSilently(silent);
+
+ if (node.isNull())
+ {
+ parserWarning("Invalid node");
+ }
+ else
+ {
+ readXUIImpl(node, block);
+ }
+}
+
+bool LLXUIParser::readXUIImpl(LLXMLNodePtr nodep, LLInitParam::BaseBlock& block)
+{
+ typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+ boost::char_separator<char> sep(".");
+
+ bool values_parsed = false;
+ bool silent = mCurReadDepth > 0;
+
+ if (nodep->getFirstChild().isNull()
+ && nodep->mAttributes.empty()
+ && nodep->getSanitizedValue().empty())
+ {
+ // empty node, just parse as flag
+ mCurReadNode = DUMMY_NODE;
+ return block.submitValue(mNameStack, *this, silent);
+ }
+
+ // submit attributes for current node
+ values_parsed |= readAttributes(nodep, block);
+
+ // treat text contents of xml node as "value" parameter
+ std::string text_contents = nodep->getSanitizedValue();
+ if (!text_contents.empty())
+ {
+ mCurReadNode = nodep;
+ mNameStack.push_back(std::make_pair(std::string("value"), true));
+ // child nodes are not necessarily valid parameters (could be a child widget)
+ // so don't complain once we've recursed
+ if (!block.submitValue(mNameStack, *this, true))
+ {
+ mNameStack.pop_back();
+ block.submitValue(mNameStack, *this, silent);
+ }
+ else
+ {
+ mNameStack.pop_back();
+ }
+ }
+
+ // then traverse children
+ // child node must start with last name of parent node (our "scope")
+ // for example: "<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, true));
+ num_tokens_pushed++;
+ }
+ else
+ {
+ // parse out "dotted" name into individual tokens
+ tokenizer name_tokens(child_name, sep);
+
+ tokenizer::iterator name_token_it = name_tokens.begin();
+ if(name_token_it == name_tokens.end())
+ {
+ childp = childp->getNextSibling();
+ continue;
+ }
+
+ // check for proper nesting
+ if (mNameStack.empty())
+ {
+ if (*name_token_it != mRootNodeName)
+ {
+ childp = childp->getNextSibling();
+ continue;
+ }
+ }
+ else if(mNameStack.back().first != *name_token_it)
+ {
+ childp = childp->getNextSibling();
+ continue;
+ }
+
+ // now ignore first token
+ ++name_token_it;
+
+ // copy remaining tokens on to our running token list
+ for(tokenizer::iterator token_to_push = name_token_it; token_to_push != name_tokens.end(); ++token_to_push)
+ {
+ mNameStack.push_back(std::make_pair(*token_to_push, true));
+ num_tokens_pushed++;
+ }
+ }
+
+ // recurse and visit children XML nodes
+ if(readXUIImpl(childp, block))
+ {
+ // child node successfully parsed, remove from DOM
+
+ values_parsed = true;
+ LLXMLNodePtr node_to_remove = childp;
+ childp = childp->getNextSibling();
+
+ nodep->deleteChild(node_to_remove);
+ }
+ else
+ {
+ childp = childp->getNextSibling();
+ }
+
+ while(num_tokens_pushed-- > 0)
+ {
+ mNameStack.pop_back();
+ }
+ }
+ mCurReadDepth--;
+ return values_parsed;
+}
+
+bool LLXUIParser::readAttributes(LLXMLNodePtr nodep, LLInitParam::BaseBlock& block)
+{
+ typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+ boost::char_separator<char> sep(".");
+
+ bool any_parsed = false;
+ bool silent = mCurReadDepth > 0;
+
+ for(LLXMLAttribList::const_iterator attribute_it = nodep->mAttributes.begin();
+ attribute_it != nodep->mAttributes.end();
+ ++attribute_it)
+ {
+ S32 num_tokens_pushed = 0;
+ std::string attribute_name(attribute_it->first->mString);
+ mCurReadNode = attribute_it->second;
+
+ tokenizer name_tokens(attribute_name, sep);
+ // copy remaining tokens on to our running token list
+ for(tokenizer::iterator token_to_push = name_tokens.begin(); token_to_push != name_tokens.end(); ++token_to_push)
+ {
+ mNameStack.push_back(std::make_pair(*token_to_push, true));
+ num_tokens_pushed++;
+ }
+
+ // child nodes are not necessarily valid attributes, so don't complain once we've recursed
+ any_parsed |= block.submitValue(mNameStack, *this, silent);
+
+ while(num_tokens_pushed-- > 0)
+ {
+ mNameStack.pop_back();
+ }
+ }
+
+ return any_parsed;
+}
+
+void LLXUIParser::writeXUIImpl(LLXMLNodePtr node, const LLInitParam::BaseBlock &block, const LLInitParam::predicate_rule_t rules, const LLInitParam::BaseBlock* diff_block)
+{
+ mWriteRootNode = node;
+ name_stack_t name_stack = Parser::name_stack_t();
+ block.serializeBlock(*this, name_stack, rules, diff_block);
+ mOutNodes.clear();
+}
+
+// go from a stack of names to a specific XML node
+LLXMLNodePtr LLXUIParser::getNode(name_stack_t& stack)
+{
+ LLXMLNodePtr out_node = mWriteRootNode;
+
+ name_stack_t::iterator next_it = stack.begin();
+ for (name_stack_t::iterator it = stack.begin();
+ it != stack.end();
+ it = next_it)
+ {
+ ++next_it;
+ bool force_new_node = false;
+
+ if (it->first.empty())
+ {
+ it->second = false;
+ continue;
+ }
+
+ if (next_it != stack.end() && next_it->first.empty() && next_it->second)
+ {
+ force_new_node = true;
+ }
+
+
+ out_nodes_t::iterator found_it = mOutNodes.find(it->first);
+
+ // node with this name not yet written
+ if (found_it == mOutNodes.end() || it->second || force_new_node)
+ {
+ // make an attribute if we are the last element on the name stack
+ bool is_attribute = next_it == stack.end();
+ LLXMLNodePtr new_node = new LLXMLNode(it->first.c_str(), is_attribute);
+ out_node->addChild(new_node);
+ mOutNodes[it->first] = new_node;
+ out_node = new_node;
+ it->second = false;
+ }
+ else
+ {
+ out_node = found_it->second;
+ }
+ }
+
+ return (out_node == mWriteRootNode ? LLXMLNodePtr(NULL) : out_node);
+}
+
+bool LLXUIParser::readFlag(Parser& parser, void* val_ptr)
+{
+ LLXUIParser& self = static_cast<LLXUIParser&>(parser);
+ return self.mCurReadNode == DUMMY_NODE;
+}
+
+bool LLXUIParser::writeFlag(Parser& parser, const void* val_ptr, name_stack_t& stack)
+{
+ // just create node
+ LLXUIParser& self = static_cast<LLXUIParser&>(parser);
+ LLXMLNodePtr node = self.getNode(stack);
+ return node.notNull();
+}
+
+bool LLXUIParser::readBoolValue(Parser& parser, void* val_ptr)
+{
+ S32 value;
+ LLXUIParser& self = static_cast<LLXUIParser&>(parser);
+ bool success = self.mCurReadNode->getBoolValue(1, &value);
+ *((bool*)val_ptr) = (value != FALSE);
+ return success;
+}
+
+bool LLXUIParser::writeBoolValue(Parser& parser, const void* val_ptr, name_stack_t& stack)
+{
+ LLXUIParser& self = static_cast<LLXUIParser&>(parser);
+ LLXMLNodePtr node = self.getNode(stack);
+ if (node.notNull())
+ {
+ node->setBoolValue(*((bool*)val_ptr));
+ return true;
+ }
+ return false;
+}
+
+bool LLXUIParser::readStringValue(Parser& parser, void* val_ptr)
+{
+ LLXUIParser& self = static_cast<LLXUIParser&>(parser);
+ *((std::string*)val_ptr) = self.mCurReadNode->getSanitizedValue();
+ return true;
+}
+
+bool LLXUIParser::writeStringValue(Parser& parser, const void* val_ptr, name_stack_t& stack)
+{
+ LLXUIParser& self = static_cast<LLXUIParser&>(parser);
+ LLXMLNodePtr node = self.getNode(stack);
+ if (node.notNull())
+ {
+ const std::string* string_val = reinterpret_cast<const std::string*>(val_ptr);
+ if (string_val->find('\n') != std::string::npos
+ || string_val->size() > MAX_STRING_ATTRIBUTE_SIZE)
+ {
+ // don't write strings with newlines into attributes
+ std::string attribute_name = node->getName()->mString;
+ LLXMLNodePtr parent_node = node->mParent;
+ parent_node->deleteChild(node);
+ // write results in text contents of node
+ if (attribute_name == "value")
+ {
+ // "value" is implicit, just write to parent
+ node = parent_node;
+ }
+ else
+ {
+ // create a child that is not an attribute, but with same name
+ node = parent_node->createChild(attribute_name.c_str(), false);
+ }
+ }
+ node->setStringValue(*string_val);
+ return true;
+ }
+ return false;
+}
+
+bool LLXUIParser::readU8Value(Parser& parser, void* val_ptr)
+{
+ LLXUIParser& self = static_cast<LLXUIParser&>(parser);
+ return self.mCurReadNode->getByteValue(1, (U8*)val_ptr);
+}
+
+bool LLXUIParser::writeU8Value(Parser& parser, const void* val_ptr, name_stack_t& stack)
+{
+ LLXUIParser& self = static_cast<LLXUIParser&>(parser);
+ LLXMLNodePtr node = self.getNode(stack);
+ if (node.notNull())
+ {
+ node->setUnsignedValue(*((U8*)val_ptr));
+ return true;
+ }
+ return false;
+}
+
+bool LLXUIParser::readS8Value(Parser& parser, void* val_ptr)
+{
+ LLXUIParser& self = static_cast<LLXUIParser&>(parser);
+ S32 value;
+ if(self.mCurReadNode->getIntValue(1, &value))
+ {
+ *((S8*)val_ptr) = value;
+ return true;
+ }
+ return false;
+}
+
+bool LLXUIParser::writeS8Value(Parser& parser, const void* val_ptr, name_stack_t& stack)
+{
+ LLXUIParser& self = static_cast<LLXUIParser&>(parser);
+ LLXMLNodePtr node = self.getNode(stack);
+ if (node.notNull())
+ {
+ node->setIntValue(*((S8*)val_ptr));
+ return true;
+ }
+ return false;
+}
+
+bool LLXUIParser::readU16Value(Parser& parser, void* val_ptr)
+{
+ LLXUIParser& self = static_cast<LLXUIParser&>(parser);
+ U32 value;
+ if(self.mCurReadNode->getUnsignedValue(1, &value))
+ {
+ *((U16*)val_ptr) = value;
+ return true;
+ }
+ return false;
+}
+
+bool LLXUIParser::writeU16Value(Parser& parser, const void* val_ptr, name_stack_t& stack)
+{
+ LLXUIParser& self = static_cast<LLXUIParser&>(parser);
+ LLXMLNodePtr node = self.getNode(stack);
+ if (node.notNull())
+ {
+ node->setUnsignedValue(*((U16*)val_ptr));
+ return true;
+ }
+ return false;
+}
+
+bool LLXUIParser::readS16Value(Parser& parser, void* val_ptr)
+{
+ LLXUIParser& self = static_cast<LLXUIParser&>(parser);
+ S32 value;
+ if(self.mCurReadNode->getIntValue(1, &value))
+ {
+ *((S16*)val_ptr) = value;
+ return true;
+ }
+ return false;
+}
+
+bool LLXUIParser::writeS16Value(Parser& parser, const void* val_ptr, name_stack_t& stack)
+{
+ LLXUIParser& self = static_cast<LLXUIParser&>(parser);
+ LLXMLNodePtr node = self.getNode(stack);
+ if (node.notNull())
+ {
+ node->setIntValue(*((S16*)val_ptr));
+ return true;
+ }
+ return false;
+}
+
+bool LLXUIParser::readU32Value(Parser& parser, void* val_ptr)
+{
+ LLXUIParser& self = static_cast<LLXUIParser&>(parser);
+ return self.mCurReadNode->getUnsignedValue(1, (U32*)val_ptr);
+}
+
+bool LLXUIParser::writeU32Value(Parser& parser, const void* val_ptr, name_stack_t& stack)
+{
+ LLXUIParser& self = static_cast<LLXUIParser&>(parser);
+ LLXMLNodePtr node = self.getNode(stack);
+ if (node.notNull())
+ {
+ node->setUnsignedValue(*((U32*)val_ptr));
+ return true;
+ }
+ return false;
+}
+
+bool LLXUIParser::readS32Value(Parser& parser, void* val_ptr)
+{
+ LLXUIParser& self = static_cast<LLXUIParser&>(parser);
+ return self.mCurReadNode->getIntValue(1, (S32*)val_ptr);
+}
+
+bool LLXUIParser::writeS32Value(Parser& parser, const void* val_ptr, name_stack_t& stack)
+{
+ LLXUIParser& self = static_cast<LLXUIParser&>(parser);
+ LLXMLNodePtr node = self.getNode(stack);
+ if (node.notNull())
+ {
+ node->setIntValue(*((S32*)val_ptr));
+ return true;
+ }
+ return false;
+}
+
+bool LLXUIParser::readF32Value(Parser& parser, void* val_ptr)
+{
+ LLXUIParser& self = static_cast<LLXUIParser&>(parser);
+ return self.mCurReadNode->getFloatValue(1, (F32*)val_ptr);
+}
+
+bool LLXUIParser::writeF32Value(Parser& parser, const void* val_ptr, name_stack_t& stack)
+{
+ LLXUIParser& self = static_cast<LLXUIParser&>(parser);
+ LLXMLNodePtr node = self.getNode(stack);
+ if (node.notNull())
+ {
+ node->setFloatValue(*((F32*)val_ptr));
+ return true;
+ }
+ return false;
+}
+
+bool LLXUIParser::readF64Value(Parser& parser, void* val_ptr)
+{
+ LLXUIParser& self = static_cast<LLXUIParser&>(parser);
+ return self.mCurReadNode->getDoubleValue(1, (F64*)val_ptr);
+}
+
+bool LLXUIParser::writeF64Value(Parser& parser, const void* val_ptr, name_stack_t& stack)
+{
+ LLXUIParser& self = static_cast<LLXUIParser&>(parser);
+ LLXMLNodePtr node = self.getNode(stack);
+ if (node.notNull())
+ {
+ node->setDoubleValue(*((F64*)val_ptr));
+ return true;
+ }
+ return false;
+}
+
+bool LLXUIParser::readVector3Value(Parser& parser, void* val_ptr)
+{
+ LLXUIParser& self = static_cast<LLXUIParser&>(parser);
+ LLVector3* vecp = (LLVector3*)val_ptr;
+ if(self.mCurReadNode->getFloatValue(3, vecp->mV) >= 3)
+ {
+ return true;
+ }
+
+ return false;
+}
+
+bool LLXUIParser::writeVector3Value(Parser& parser, const void* val_ptr, name_stack_t& stack)
+{
+ LLXUIParser& self = static_cast<LLXUIParser&>(parser);
+ LLXMLNodePtr node = self.getNode(stack);
+ if (node.notNull())
+ {
+ LLVector3 vector = *((LLVector3*)val_ptr);
+ node->setFloatValue(3, vector.mV);
+ return true;
+ }
+ return false;
+}
+
+bool LLXUIParser::readColor4Value(Parser& parser, void* val_ptr)
+{
+ LLXUIParser& self = static_cast<LLXUIParser&>(parser);
+ LLColor4* colorp = (LLColor4*)val_ptr;
+ if(self.mCurReadNode->getFloatValue(4, colorp->mV) >= 3)
+ {
+ return true;
+ }
+
+ return false;
+}
+
+bool LLXUIParser::writeColor4Value(Parser& parser, const void* val_ptr, name_stack_t& stack)
+{
+ LLXUIParser& self = static_cast<LLXUIParser&>(parser);
+ LLXMLNodePtr node = self.getNode(stack);
+ if (node.notNull())
+ {
+ LLColor4 color = *((LLColor4*)val_ptr);
+ node->setFloatValue(4, color.mV);
+ return true;
+ }
+ return false;
+}
+
+bool LLXUIParser::readUIColorValue(Parser& parser, void* val_ptr)
+{
+ LLXUIParser& self = static_cast<LLXUIParser&>(parser);
+ LLUIColor* param = (LLUIColor*)val_ptr;
+ LLColor4 color;
+ bool success = self.mCurReadNode->getFloatValue(4, color.mV) >= 3;
+ if (success)
+ {
+ param->set(color);
+ return true;
+ }
+ return false;
+}
+
+bool LLXUIParser::writeUIColorValue(Parser& parser, const void* val_ptr, name_stack_t& stack)
+{
+ LLXUIParser& self = static_cast<LLXUIParser&>(parser);
+ LLXMLNodePtr node = self.getNode(stack);
+ if (node.notNull())
+ {
+ LLUIColor color = *((LLUIColor*)val_ptr);
+ //RN: don't write out the color that is represented by a function
+ // rely on param block exporting to get the reference to the color settings
+ if (color.isReference()) return false;
+ node->setFloatValue(4, color.get().mV);
+ return true;
+ }
+ return false;
+}
+
+bool LLXUIParser::readUUIDValue(Parser& parser, void* val_ptr)
+{
+ LLXUIParser& self = static_cast<LLXUIParser&>(parser);
+ LLUUID temp_id;
+ // LLUUID::set is destructive, so use temporary value
+ if (temp_id.set(self.mCurReadNode->getSanitizedValue()))
+ {
+ *(LLUUID*)(val_ptr) = temp_id;
+ return true;
+ }
+ return false;
+}
+
+bool LLXUIParser::writeUUIDValue(Parser& parser, const void* val_ptr, name_stack_t& stack)
+{
+ LLXUIParser& self = static_cast<LLXUIParser&>(parser);
+ LLXMLNodePtr node = self.getNode(stack);
+ if (node.notNull())
+ {
+ node->setStringValue(((LLUUID*)val_ptr)->asString());
+ return true;
+ }
+ return false;
+}
+
+bool LLXUIParser::readSDValue(Parser& parser, void* val_ptr)
+{
+ LLXUIParser& self = static_cast<LLXUIParser&>(parser);
+ *((LLSD*)val_ptr) = LLSD(self.mCurReadNode->getSanitizedValue());
+ return true;
+}
+
+bool LLXUIParser::writeSDValue(Parser& parser, const void* val_ptr, name_stack_t& stack)
+{
+ LLXUIParser& self = static_cast<LLXUIParser&>(parser);
+
+ LLXMLNodePtr node = self.getNode(stack);
+ if (node.notNull())
+ {
+ std::string string_val = ((LLSD*)val_ptr)->asString();
+ if (string_val.find('\n') != std::string::npos || string_val.size() > MAX_STRING_ATTRIBUTE_SIZE)
+ {
+ // don't write strings with newlines into attributes
+ std::string attribute_name = node->getName()->mString;
+ LLXMLNodePtr parent_node = node->mParent;
+ parent_node->deleteChild(node);
+ // write results in text contents of node
+ if (attribute_name == "value")
+ {
+ // "value" is implicit, just write to parent
+ node = parent_node;
+ }
+ else
+ {
+ node = parent_node->createChild(attribute_name.c_str(), false);
+ }
+ }
+
+ node->setStringValue(string_val);
+ return true;
+ }
+ return false;
+}
+
+/*virtual*/ std::string LLXUIParser::getCurrentElementName()
+{
+ std::string full_name;
+ for (name_stack_t::iterator it = mNameStack.begin();
+ it != mNameStack.end();
+ ++it)
+ {
+ full_name += it->first + "."; // build up dotted names: "button.param.nestedparam."
+ }
+
+ return full_name;
+}
+
+void LLXUIParser::parserWarning(const std::string& message)
+{
+#ifdef LL_WINDOWS
+ // use Visual Studio friendly formatting of output message for easy access to originating xml
+ LL_INFOS() << llformat("%s(%d):\t%s", mCurFileName.c_str(), mCurReadNode->getLineNumber(), message.c_str()) << LL_ENDL;
+#else
+ Parser::parserWarning(message);
+#endif
+}
+
+void LLXUIParser::parserError(const std::string& message)
+{
+#ifdef LL_WINDOWS
+ // use Visual Studio friendly formatting of output message for easy access to originating xml
+ LL_INFOS() << llformat("%s(%d):\t%s", mCurFileName.c_str(), mCurReadNode->getLineNumber(), message.c_str()) << LL_ENDL;
+#else
+ Parser::parserError(message);
+#endif
+}
+
+
+//
+// LLSimpleXUIParser
+//
+
+struct ScopedFile
+{
+ ScopedFile( const std::string& filename, const char* accessmode )
+ {
+ mFile = LLFile::fopen(filename, accessmode);
+ }
+
+ ~ScopedFile()
+ {
+ fclose(mFile);
+ mFile = NULL;
+ }
+
+ S32 getRemainingBytes()
+ {
+ if (!isOpen()) return 0;
+
+ S32 cur_pos = ftell(mFile);
+ fseek(mFile, 0L, SEEK_END);
+ S32 file_size = ftell(mFile);
+ fseek(mFile, cur_pos, SEEK_SET);
+ return file_size - cur_pos;
+ }
+
+ bool isOpen() { return mFile != NULL; }
+
+ LLFILE* mFile;
+};
+LLSimpleXUIParser::LLSimpleXUIParser(LLSimpleXUIParser::element_start_callback_t element_cb)
+: Parser(sSimpleXUIReadFuncs, sSimpleXUIWriteFuncs, sSimpleXUIInspectFuncs),
+ mCurReadDepth(0),
+ mElementCB(element_cb)
+{
+ if (sSimpleXUIReadFuncs.empty())
+ {
+ registerParserFuncs<LLInitParam::Flag>(readFlag);
+ registerParserFuncs<bool>(readBoolValue);
+ registerParserFuncs<std::string>(readStringValue);
+ registerParserFuncs<U8>(readU8Value);
+ registerParserFuncs<S8>(readS8Value);
+ registerParserFuncs<U16>(readU16Value);
+ registerParserFuncs<S16>(readS16Value);
+ registerParserFuncs<U32>(readU32Value);
+ registerParserFuncs<S32>(readS32Value);
+ registerParserFuncs<F32>(readF32Value);
+ registerParserFuncs<F64>(readF64Value);
+ registerParserFuncs<LLColor4>(readColor4Value);
+ registerParserFuncs<LLUIColor>(readUIColorValue);
+ registerParserFuncs<LLUUID>(readUUIDValue);
+ registerParserFuncs<LLSD>(readSDValue);
+ }
+}
+
+LLSimpleXUIParser::~LLSimpleXUIParser()
+{
+}
+
+
+bool LLSimpleXUIParser::readXUI(const std::string& filename, LLInitParam::BaseBlock& block, bool silent)
+{
+ LL_RECORD_BLOCK_TIME(FTM_PARSE_XUI);
+
+ mParser = XML_ParserCreate(NULL);
+ XML_SetUserData(mParser, this);
+ XML_SetElementHandler( mParser, startElementHandler, endElementHandler);
+ XML_SetCharacterDataHandler( mParser, characterDataHandler);
+
+ mOutputStack.push_back(std::make_pair(&block, 0));
+ mNameStack.clear();
+ mCurFileName = filename;
+ mCurReadDepth = 0;
+ setParseSilently(silent);
+
+ ScopedFile file(filename, "rb");
+ if( !file.isOpen() )
+ {
+ LL_WARNS("ReadXUI") << "Unable to open file " << filename << LL_ENDL;
+ XML_ParserFree( mParser );
+ return false;
+ }
+
+ S32 bytes_read = 0;
+
+ S32 buffer_size = file.getRemainingBytes();
+ void* buffer = XML_GetBuffer(mParser, buffer_size);
+ if( !buffer )
+ {
+ LL_WARNS("ReadXUI") << "Unable to allocate XML buffer while reading file " << filename << LL_ENDL;
+ XML_ParserFree( mParser );
+ return false;
+ }
+
+ bytes_read = (S32)fread(buffer, 1, buffer_size, file.mFile);
+ if( bytes_read <= 0 )
+ {
+ LL_WARNS("ReadXUI") << "Error while reading file " << filename << LL_ENDL;
+ XML_ParserFree( mParser );
+ return false;
+ }
+
+ mEmptyLeafNode.push_back(false);
+
+ if( !XML_ParseBuffer(mParser, bytes_read, TRUE ) )
+ {
+ LL_WARNS("ReadXUI") << "Error while parsing file " << filename << LL_ENDL;
+ XML_ParserFree( mParser );
+ return false;
+ }
+
+ mEmptyLeafNode.pop_back();
+
+ XML_ParserFree( mParser );
+ return true;
+}
+
+void LLSimpleXUIParser::startElementHandler(void *userData, const char *name, const char **atts)
+{
+ LLSimpleXUIParser* self = reinterpret_cast<LLSimpleXUIParser*>(userData);
+ self->startElement(name, atts);
+}
+
+void LLSimpleXUIParser::endElementHandler(void *userData, const char *name)
+{
+ LLSimpleXUIParser* self = reinterpret_cast<LLSimpleXUIParser*>(userData);
+ self->endElement(name);
+}
+
+void LLSimpleXUIParser::characterDataHandler(void *userData, const char *s, int len)
+{
+ LLSimpleXUIParser* self = reinterpret_cast<LLSimpleXUIParser*>(userData);
+ self->characterData(s, len);
+}
+
+void LLSimpleXUIParser::characterData(const char *s, int len)
+{
+ mTextContents += std::string(s, len);
+}
+
+void LLSimpleXUIParser::startElement(const char *name, const char **atts)
+{
+ processText();
+
+ typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+ boost::char_separator<char> sep(".");
+
+ if (mElementCB)
+ {
+ LLInitParam::BaseBlock* blockp = mElementCB(*this, name);
+ if (blockp)
+ {
+ mOutputStack.push_back(std::make_pair(blockp, 0));
+ }
+ }
+
+ mOutputStack.back().second++;
+ S32 num_tokens_pushed = 0;
+ std::string child_name(name);
+
+ if (mOutputStack.back().second == 1)
+ { // root node for this block
+ mScope.push_back(child_name);
+ }
+ else
+ { // compound attribute
+ if (child_name.find(".") == std::string::npos)
+ {
+ mNameStack.push_back(std::make_pair(child_name, true));
+ num_tokens_pushed++;
+ mScope.push_back(child_name);
+ }
+ else
+ {
+ // parse out "dotted" name into individual tokens
+ tokenizer name_tokens(child_name, sep);
+
+ tokenizer::iterator name_token_it = name_tokens.begin();
+ if(name_token_it == name_tokens.end())
+ {
+ return;
+ }
+
+ // check for proper nesting
+ if(!mScope.empty() && *name_token_it != mScope.back())
+ {
+ return;
+ }
+
+ // now ignore first token
+ ++name_token_it;
+
+ // copy remaining tokens on to our running token list
+ for(tokenizer::iterator token_to_push = name_token_it; token_to_push != name_tokens.end(); ++token_to_push)
+ {
+ mNameStack.push_back(std::make_pair(*token_to_push, true));
+ num_tokens_pushed++;
+ }
+ mScope.push_back(mNameStack.back().first);
+ }
+ }
+
+ // parent node is not empty
+ mEmptyLeafNode.back() = false;
+ // we are empty if we have no attributes
+ mEmptyLeafNode.push_back(atts[0] == NULL);
+
+ mTokenSizeStack.push_back(num_tokens_pushed);
+ readAttributes(atts);
+
+}
+
+void LLSimpleXUIParser::endElement(const char *name)
+{
+ bool has_text = processText();
+
+ // no text, attributes, or children
+ if (!has_text && mEmptyLeafNode.back())
+ {
+ // submit this as a valueless name (even though there might be text contents we haven't seen yet)
+ mCurAttributeValueBegin = NO_VALUE_MARKER;
+ mOutputStack.back().first->submitValue(mNameStack, *this, mParseSilently);
+ }
+
+ if (--mOutputStack.back().second == 0)
+ {
+ if (mOutputStack.empty())
+ {
+ LL_ERRS("ReadXUI") << "Parameter block output stack popped while empty." << LL_ENDL;
+ }
+ mOutputStack.pop_back();
+ }
+
+ S32 num_tokens_to_pop = mTokenSizeStack.back();
+ mTokenSizeStack.pop_back();
+ while(num_tokens_to_pop-- > 0)
+ {
+ mNameStack.pop_back();
+ }
+ mScope.pop_back();
+ mEmptyLeafNode.pop_back();
+}
+
+bool LLSimpleXUIParser::readAttributes(const char **atts)
+{
+ typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+ boost::char_separator<char> sep(".");
+
+ bool any_parsed = false;
+ for(S32 i = 0; atts[i] && atts[i+1]; i += 2 )
+ {
+ std::string attribute_name(atts[i]);
+ mCurAttributeValueBegin = atts[i+1];
+
+ S32 num_tokens_pushed = 0;
+ tokenizer name_tokens(attribute_name, sep);
+ // copy remaining tokens on to our running token list
+ for(tokenizer::iterator token_to_push = name_tokens.begin(); token_to_push != name_tokens.end(); ++token_to_push)
+ {
+ mNameStack.push_back(std::make_pair(*token_to_push, true));
+ num_tokens_pushed++;
+ }
+
+ // child nodes are not necessarily valid attributes, so don't complain once we've recursed
+ any_parsed |= mOutputStack.back().first->submitValue(mNameStack, *this, mParseSilently);
+
+ while(num_tokens_pushed-- > 0)
+ {
+ mNameStack.pop_back();
+ }
+ }
+ return any_parsed;
+}
+
+bool LLSimpleXUIParser::processText()
+{
+ if (!mTextContents.empty())
+ {
+ LLStringUtil::trim(mTextContents);
+ if (!mTextContents.empty())
+ {
+ mNameStack.push_back(std::make_pair(std::string("value"), true));
+ mCurAttributeValueBegin = mTextContents.c_str();
+ mOutputStack.back().first->submitValue(mNameStack, *this, mParseSilently);
+ mNameStack.pop_back();
+ }
+ mTextContents.clear();
+ return true;
+ }
+ return false;
+}
+
+/*virtual*/ std::string LLSimpleXUIParser::getCurrentElementName()
+{
+ std::string full_name;
+ for (name_stack_t::iterator it = mNameStack.begin();
+ it != mNameStack.end();
+ ++it)
+ {
+ full_name += it->first + "."; // build up dotted names: "button.param.nestedparam."
+ }
+
+ return full_name;
+}
+
+void LLSimpleXUIParser::parserWarning(const std::string& message)
+{
+#ifdef LL_WINDOWS
+ // use Visual Studio friendly formatting of output message for easy access to originating xml
+ LL_INFOS() << llformat("%s(%d):\t%s", mCurFileName.c_str(), LINE_NUMBER_HERE, message.c_str()) << LL_ENDL;
+#else
+ Parser::parserWarning(message);
+#endif
+}
+
+void LLSimpleXUIParser::parserError(const std::string& message)
+{
+#ifdef LL_WINDOWS
+ // use Visual Studio friendly formatting of output message for easy access to originating xml
+ LL_INFOS() << llformat("%s(%d):\t%s", mCurFileName.c_str(), LINE_NUMBER_HERE, message.c_str()) << LL_ENDL;
+#else
+ Parser::parserError(message);
+#endif
+}
+
+bool LLSimpleXUIParser::readFlag(Parser& parser, void* val_ptr)
+{
+ LLSimpleXUIParser& self = static_cast<LLSimpleXUIParser&>(parser);
+ return self.mCurAttributeValueBegin == NO_VALUE_MARKER;
+}
+
+bool LLSimpleXUIParser::readBoolValue(Parser& parser, void* val_ptr)
+{
+ LLSimpleXUIParser& self = static_cast<LLSimpleXUIParser&>(parser);
+ if (!strcmp(self.mCurAttributeValueBegin, "true"))
+ {
+ *((bool*)val_ptr) = true;
+ return true;
+ }
+ else if (!strcmp(self.mCurAttributeValueBegin, "false"))
+ {
+ *((bool*)val_ptr) = false;
+ return true;
+ }
+
+ return false;
+}
+
+bool LLSimpleXUIParser::readStringValue(Parser& parser, void* val_ptr)
+{
+ LLSimpleXUIParser& self = static_cast<LLSimpleXUIParser&>(parser);
+ *((std::string*)val_ptr) = self.mCurAttributeValueBegin;
+ return true;
+}
+
+bool LLSimpleXUIParser::readU8Value(Parser& parser, void* val_ptr)
+{
+ LLSimpleXUIParser& self = static_cast<LLSimpleXUIParser&>(parser);
+ return parse(self.mCurAttributeValueBegin, uint_p[assign_a(*(U8*)val_ptr)]).full;
+}
+
+bool LLSimpleXUIParser::readS8Value(Parser& parser, void* val_ptr)
+{
+ LLSimpleXUIParser& self = static_cast<LLSimpleXUIParser&>(parser);
+ return parse(self.mCurAttributeValueBegin, int_p[assign_a(*(S8*)val_ptr)]).full;
+}
+
+bool LLSimpleXUIParser::readU16Value(Parser& parser, void* val_ptr)
+{
+ LLSimpleXUIParser& self = static_cast<LLSimpleXUIParser&>(parser);
+ return parse(self.mCurAttributeValueBegin, uint_p[assign_a(*(U16*)val_ptr)]).full;
+}
+
+bool LLSimpleXUIParser::readS16Value(Parser& parser, void* val_ptr)
+{
+ LLSimpleXUIParser& self = static_cast<LLSimpleXUIParser&>(parser);
+ return parse(self.mCurAttributeValueBegin, int_p[assign_a(*(S16*)val_ptr)]).full;
+}
+
+bool LLSimpleXUIParser::readU32Value(Parser& parser, void* val_ptr)
+{
+ LLSimpleXUIParser& self = static_cast<LLSimpleXUIParser&>(parser);
+ return parse(self.mCurAttributeValueBegin, uint_p[assign_a(*(U32*)val_ptr)]).full;
+}
+
+bool LLSimpleXUIParser::readS32Value(Parser& parser, void* val_ptr)
+{
+ LLSimpleXUIParser& self = static_cast<LLSimpleXUIParser&>(parser);
+ return parse(self.mCurAttributeValueBegin, int_p[assign_a(*(S32*)val_ptr)]).full;
+}
+
+bool LLSimpleXUIParser::readF32Value(Parser& parser, void* val_ptr)
+{
+ LLSimpleXUIParser& self = static_cast<LLSimpleXUIParser&>(parser);
+ return parse(self.mCurAttributeValueBegin, real_p[assign_a(*(F32*)val_ptr)]).full;
+}
+
+bool LLSimpleXUIParser::readF64Value(Parser& parser, void* val_ptr)
+{
+ LLSimpleXUIParser& self = static_cast<LLSimpleXUIParser&>(parser);
+ return parse(self.mCurAttributeValueBegin, real_p[assign_a(*(F64*)val_ptr)]).full;
+}
+
+bool LLSimpleXUIParser::readColor4Value(Parser& parser, void* val_ptr)
+{
+ LLSimpleXUIParser& self = static_cast<LLSimpleXUIParser&>(parser);
+ LLColor4 value;
+
+ if (parse(self.mCurAttributeValueBegin, real_p[assign_a(value.mV[0])] >> real_p[assign_a(value.mV[1])] >> real_p[assign_a(value.mV[2])] >> real_p[assign_a(value.mV[3])], space_p).full)
+ {
+ *(LLColor4*)(val_ptr) = value;
+ return true;
+ }
+ return false;
+}
+
+bool LLSimpleXUIParser::readUIColorValue(Parser& parser, void* val_ptr)
+{
+ LLSimpleXUIParser& self = static_cast<LLSimpleXUIParser&>(parser);
+ LLColor4 value;
+ LLUIColor* colorp = (LLUIColor*)val_ptr;
+
+ if (parse(self.mCurAttributeValueBegin, real_p[assign_a(value.mV[0])] >> real_p[assign_a(value.mV[1])] >> real_p[assign_a(value.mV[2])] >> real_p[assign_a(value.mV[3])], space_p).full)
+ {
+ colorp->set(value);
+ return true;
+ }
+ return false;
+}
+
+bool LLSimpleXUIParser::readUUIDValue(Parser& parser, void* val_ptr)
+{
+ LLSimpleXUIParser& self = static_cast<LLSimpleXUIParser&>(parser);
+ LLUUID temp_id;
+ // LLUUID::set is destructive, so use temporary value
+ if (temp_id.set(std::string(self.mCurAttributeValueBegin)))
+ {
+ *(LLUUID*)(val_ptr) = temp_id;
+ return true;
+ }
+ return false;
+}
+
+bool LLSimpleXUIParser::readSDValue(Parser& parser, void* val_ptr)
+{
+ LLSimpleXUIParser& self = static_cast<LLSimpleXUIParser&>(parser);
+ *((LLSD*)val_ptr) = LLSD(self.mCurAttributeValueBegin);
+ return true;
+}
diff --git a/indra/llui/llxuiparser.h b/indra/llui/llxuiparser.h
new file mode 100755
index 0000000000..ad2a39cab7
--- /dev/null
+++ b/indra/llui/llxuiparser.h
@@ -0,0 +1,254 @@
+/**
+ * @file llxuiparser.h
+ * @brief Utility functions for handling XUI structures in XML
+ *
+ * $LicenseInfo:firstyear=2003&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LLXUIPARSER_H
+#define LLXUIPARSER_H
+
+#include "llinitparam.h"
+#include "llregistry.h"
+#include "llxmlnode.h"
+
+#include <boost/function.hpp>
+#include <iosfwd>
+#include <stack>
+#include <set>
+
+class LLView;
+
+// lookup widget type by name
+class LLWidgetTypeRegistry
+: public LLRegistrySingleton<std::string, const std::type_info*, LLWidgetTypeRegistry>
+{};
+
+
+// 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;
+
+class LLChildRegistryRegistry
+: public LLRegistrySingleton<const std::type_info*, widget_registry_t, LLChildRegistryRegistry>
+{};
+
+class LLXSDWriter : public LLInitParam::Parser
+{
+ LOG_CLASS(LLXSDWriter);
+public:
+ void writeXSD(const std::string& name, LLXMLNodePtr node, const LLInitParam::BaseBlock& block, const std::string& xml_namespace);
+
+ /*virtual*/ std::string getCurrentElementName() { return LLStringUtil::null; }
+
+ LLXSDWriter();
+ ~LLXSDWriter();
+
+protected:
+ void writeAttribute(const std::string& type, const Parser::name_stack_t&, S32 min_count, S32 max_count, const std::vector<std::string>* possible_values);
+ void addAttributeToSchema(LLXMLNodePtr nodep, const std::string& attribute_name, const std::string& type, bool mandatory, const std::vector<std::string>* possible_values);
+ LLXMLNodePtr mAttributeNode;
+ LLXMLNodePtr mElementNode;
+ LLXMLNodePtr mSchemaNode;
+
+ typedef std::set<std::string> string_set_t;
+ typedef std::map<LLXMLNodePtr, string_set_t> attributes_map_t;
+ attributes_map_t mAttributesWritten;
+};
+
+
+
+// NOTE: DOES NOT WORK YET
+// should support child widgets for XUI
+class LLXUIXSDWriter : public LLXSDWriter
+{
+public:
+ void writeXSD(const std::string& name, const std::string& path, const LLInitParam::BaseBlock& block);
+};
+
+
+class LLXUIParserImpl;
+
+class LLXUIParser : public LLInitParam::Parser
+{
+LOG_CLASS(LLXUIParser);
+
+public:
+ LLXUIParser();
+ 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, const std::string& filename = LLStringUtil::null, bool silent=false);
+ template<typename BLOCK>
+ void writeXUI(LLXMLNodePtr node,
+ const BLOCK& block,
+ const LLInitParam::predicate_rule_t rules = LLInitParam::default_parse_rules(),
+ const LLInitParam::BaseBlock* diff_block = NULL)
+ {
+ if (!diff_block
+ && !rules.isAmbivalent(LLInitParam::HAS_DEFAULT_VALUE))
+ {
+ diff_block = &LLInitParam::defaultValue<BLOCK>();
+ }
+ writeXUIImpl(node, block, rules, diff_block);
+ }
+
+private:
+ LLXUIParser(const LLXUIParser& other); // no-copy
+ void writeXUIImpl(LLXMLNodePtr node,
+ const LLInitParam::BaseBlock& block,
+ const LLInitParam::predicate_rule_t rules,
+ const LLInitParam::BaseBlock* diff_block);
+ bool readXUIImpl(LLXMLNodePtr node, LLInitParam::BaseBlock& block);
+ bool readAttributes(LLXMLNodePtr nodep, LLInitParam::BaseBlock& block);
+
+ //reader helper functions
+ static bool readFlag(Parser& parser, void* val_ptr);
+ static bool readBoolValue(Parser& parser, void* val_ptr);
+ static bool readStringValue(Parser& parser, void* val_ptr);
+ static bool readU8Value(Parser& parser, void* val_ptr);
+ static bool readS8Value(Parser& parser, void* val_ptr);
+ static bool readU16Value(Parser& parser, void* val_ptr);
+ static bool readS16Value(Parser& parser, void* val_ptr);
+ static bool readU32Value(Parser& parser, void* val_ptr);
+ static bool readS32Value(Parser& parser, void* val_ptr);
+ static bool readF32Value(Parser& parser, void* val_ptr);
+ static bool readF64Value(Parser& parser, void* val_ptr);
+ static bool readVector3Value(Parser& parser, void* val_ptr);
+ static bool readColor4Value(Parser& parser, void* val_ptr);
+ static bool readUIColorValue(Parser& parser, void* val_ptr);
+ static bool readUUIDValue(Parser& parser, void* val_ptr);
+ static bool readSDValue(Parser& parser, void* val_ptr);
+
+ //writer helper functions
+ static bool writeFlag(Parser& parser, const void* val_ptr, name_stack_t&);
+ static bool writeBoolValue(Parser& parser, const void* val_ptr, name_stack_t&);
+ static bool writeStringValue(Parser& parser, const void* val_ptr, name_stack_t&);
+ static bool writeU8Value(Parser& parser, const void* val_ptr, name_stack_t&);
+ static bool writeS8Value(Parser& parser, const void* val_ptr, name_stack_t&);
+ static bool writeU16Value(Parser& parser, const void* val_ptr, name_stack_t&);
+ static bool writeS16Value(Parser& parser, const void* val_ptr, name_stack_t&);
+ static bool writeU32Value(Parser& parser, const void* val_ptr, name_stack_t&);
+ static bool writeS32Value(Parser& parser, const void* val_ptr, name_stack_t&);
+ static bool writeF32Value(Parser& parser, const void* val_ptr, name_stack_t&);
+ static bool writeF64Value(Parser& parser, const void* val_ptr, name_stack_t&);
+ static bool writeVector3Value(Parser& parser, const void* val_ptr, name_stack_t&);
+ static bool writeColor4Value(Parser& parser, const void* val_ptr, name_stack_t&);
+ static bool writeUIColorValue(Parser& parser, const void* val_ptr, name_stack_t&);
+ static bool writeUUIDValue(Parser& parser, const void* val_ptr, name_stack_t&);
+ static bool writeSDValue(Parser& parser, const void* val_ptr, name_stack_t&);
+
+ LLXMLNodePtr getNode(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;
+
+ typedef std::map<std::string, LLXMLNodePtr> out_nodes_t;
+ out_nodes_t mOutNodes;
+ LLXMLNodePtr mLastWrittenChild;
+ S32 mCurReadDepth;
+ std::string mCurFileName;
+ std::string mRootNodeName;
+};
+
+// LLSimpleXUIParser is a streamlined SAX-based XUI parser that does not support localization
+// or parsing of a tree of independent param blocks, such as child widgets.
+// Use this for reading non-localized files that only need a single param block as a result.
+//
+// NOTE: In order to support nested block parsing, we need callbacks for start element that
+// push new blocks contexts on the mScope stack.
+// NOTE: To support localization without building a DOM, we need to enforce consistent
+// ordering of child elements from base file to localized diff file. Then we can use a pair
+// of coroutines to perform matching of xml nodes during parsing. Not sure if the overhead
+// of coroutines would offset the gain from SAX parsing
+class LLSimpleXUIParserImpl;
+
+class LLSimpleXUIParser : public LLInitParam::Parser
+{
+LOG_CLASS(LLSimpleXUIParser);
+public:
+ typedef LLInitParam::Parser::name_stack_t name_stack_t;
+ typedef LLInitParam::BaseBlock* (*element_start_callback_t)(LLSimpleXUIParser&, const char* block_name);
+
+ LLSimpleXUIParser(element_start_callback_t element_cb = NULL);
+ virtual ~LLSimpleXUIParser();
+
+ /*virtual*/ std::string getCurrentElementName();
+ /*virtual*/ void parserWarning(const std::string& message);
+ /*virtual*/ void parserError(const std::string& message);
+
+ bool readXUI(const std::string& filename, LLInitParam::BaseBlock& block, bool silent=false);
+
+
+private:
+ //reader helper functions
+ static bool readFlag(Parser&, void* val_ptr);
+ static bool readBoolValue(Parser&, void* val_ptr);
+ static bool readStringValue(Parser&, void* val_ptr);
+ static bool readU8Value(Parser&, void* val_ptr);
+ static bool readS8Value(Parser&, void* val_ptr);
+ static bool readU16Value(Parser&, void* val_ptr);
+ static bool readS16Value(Parser&, void* val_ptr);
+ static bool readU32Value(Parser&, void* val_ptr);
+ static bool readS32Value(Parser&, void* val_ptr);
+ static bool readF32Value(Parser&, void* val_ptr);
+ static bool readF64Value(Parser&, void* val_ptr);
+ static bool readColor4Value(Parser&, void* val_ptr);
+ static bool readUIColorValue(Parser&, void* val_ptr);
+ static bool readUUIDValue(Parser&, void* val_ptr);
+ static bool readSDValue(Parser&, void* val_ptr);
+
+private:
+ static void startElementHandler(void *userData, const char *name, const char **atts);
+ static void endElementHandler(void *userData, const char *name);
+ static void characterDataHandler(void *userData, const char *s, int len);
+
+ void startElement(const char *name, const char **atts);
+ void endElement(const char *name);
+ void characterData(const char *s, int len);
+ bool readAttributes(const char **atts);
+ bool processText();
+
+ Parser::name_stack_t mNameStack;
+ struct XML_ParserStruct* mParser;
+ LLXMLNodePtr mLastWrittenChild;
+ S32 mCurReadDepth;
+ std::string mCurFileName;
+ std::string mTextContents;
+ const char* mCurAttributeValueBegin;
+ std::vector<S32> mTokenSizeStack;
+ std::vector<std::string> mScope;
+ std::vector<bool> mEmptyLeafNode;
+ element_start_callback_t mElementCB;
+
+ std::vector<std::pair<LLInitParam::BaseBlock*, S32> > mOutputStack;
+};
+
+
+#endif //LLXUIPARSER_H
diff --git a/indra/llui/tests/llurlentry_stub.cpp b/indra/llui/tests/llurlentry_stub.cpp
index 58b27671e8..5d3f9ac327 100644..100755
--- a/indra/llui/tests/llurlentry_stub.cpp
+++ b/indra/llui/tests/llurlentry_stub.cpp
@@ -40,14 +40,10 @@ bool LLAvatarNameCache::get(const LLUUID& agent_id, LLAvatarName *av_name)
return false;
}
-void LLAvatarNameCache::get(const LLUUID& agent_id, callback_slot_t slot)
+LLAvatarNameCache::callback_connection_t LLAvatarNameCache::get(const LLUUID& agent_id, callback_slot_t slot)
{
- return;
-}
-
-bool LLAvatarNameCache::useDisplayNames()
-{
- return false;
+ callback_connection_t connection;
+ return connection;
}
//
@@ -105,41 +101,14 @@ LLStyle::Params::Params()
namespace LLInitParam
{
- BaseBlock::BaseBlock() {}
- BaseBlock::~BaseBlock() {}
- Param::Param(BaseBlock* enclosing_block)
- : mIsProvided(false)
- {
- const U8* my_addr = reinterpret_cast<const U8*>(this);
- const U8* block_addr = reinterpret_cast<const U8*>(enclosing_block);
- mEnclosingBlockOffset = (U16)(my_addr - block_addr);
- }
- void BaseBlock::setLastChangedParam(const Param& last_param, bool user_provided) {}
-
- void BaseBlock::addParam(BlockDescriptor& block_data, const ParamDescriptor& in_param, const char* char_name){}
- param_handle_t BaseBlock::getHandleFromParam(const Param* param) const {return 0;}
-
- void BaseBlock::init(BlockDescriptor& descriptor, BlockDescriptor& base_descriptor, size_t block_size)
- {
- descriptor.mCurrentBlockPtr = this;
- }
- bool BaseBlock::deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack){ return true; }
- bool BaseBlock::serializeBlock(Parser& parser, Parser::name_stack_t name_stack, const LLInitParam::BaseBlock* diff_block) const { return true; }
- bool BaseBlock::inspectBlock(Parser& parser, Parser::name_stack_t name_stack) const { return true; }
- bool BaseBlock::merge(BlockDescriptor& block_data, const BaseBlock& other, bool overwrite) { return true; }
- bool BaseBlock::validateBlock(bool emit_errors) const { return true; }
-
- 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)
+ ParamValue<LLUIColor>::ParamValue(const LLUIColor& color)
+ : super_t(color)
{}
- void TypedParam<LLUIColor>::setValueFromBlock() const
+ void ParamValue<LLUIColor>::updateValueFromBlock()
{}
- void TypedParam<LLUIColor>::setBlockFromValue()
- {}
-
- void TypeValues<LLUIColor>::declareValues()
+ void ParamValue<LLUIColor>::updateBlockFromValue(bool)
{}
bool ParamCompare<const LLFontGL*, false>::equals(const LLFontGL* a, const LLFontGL* b)
@@ -147,14 +116,14 @@ namespace LLInitParam
return false;
}
- 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)
+ ParamValue<const LLFontGL*>::ParamValue(const LLFontGL* fontp)
+ : super_t(fontp)
{}
- void TypedParam<const LLFontGL*>::setValueFromBlock() const
+ void ParamValue<const LLFontGL*>::updateValueFromBlock()
{}
- void TypedParam<const LLFontGL*>::setBlockFromValue()
+ void ParamValue<const LLFontGL*>::updateBlockFromValue(bool)
{}
void TypeValues<LLFontGL::HAlign>::declareValues()
@@ -166,10 +135,10 @@ namespace LLInitParam
void TypeValues<LLFontGL::ShadowType>::declareValues()
{}
- void TypedParam<LLUIImage*>::setValueFromBlock() const
+ void ParamValue<LLUIImage*>::updateValueFromBlock()
{}
- void TypedParam<LLUIImage*>::setBlockFromValue()
+ void ParamValue<LLUIImage*>::updateBlockFromValue(bool)
{}
@@ -193,8 +162,8 @@ LLFontGL* LLFontGL::getFontDefault()
return NULL;
}
-char* _PREHASH_AgentData = (char *) "AgentData";
-char* _PREHASH_AgentID = (char *) "AgentID";
+char const* const _PREHASH_AgentData = (char *)"AgentData";
+char const* const _PREHASH_AgentID = (char *)"AgentID";
LLHost LLHost::invalid(INVALID_PORT,INVALID_HOST_IP_ADDRESS);
diff --git a/indra/llui/tests/llurlentry_test.cpp b/indra/llui/tests/llurlentry_test.cpp
index 8f0a48018f..c3f0e92cb0 100644..100755
--- a/indra/llui/tests/llurlentry_test.cpp
+++ b/indra/llui/tests/llurlentry_test.cpp
@@ -31,7 +31,7 @@
#include "llurlentry_stub.cpp"
#include "lltut.h"
#include "../lluicolortable.h"
-#include "../lluiimage.h"
+#include "../llrender/lluiimage.h"
#include <boost/regex.hpp>
diff --git a/indra/llui/tests/llurlmatch_test.cpp b/indra/llui/tests/llurlmatch_test.cpp
index fdaab00f18..843886eb69 100644..100755
--- a/indra/llui/tests/llurlmatch_test.cpp
+++ b/indra/llui/tests/llurlmatch_test.cpp
@@ -28,7 +28,7 @@
#include "linden_common.h"
#include "../llurlmatch.h"
-#include "../lluiimage.h"
+#include "../llrender/lluiimage.h"
#include "lltut.h"
// link seams
@@ -63,44 +63,14 @@ S32 LLUIImage::getHeight() const
namespace LLInitParam
{
- BaseBlock::BaseBlock() {}
- BaseBlock::~BaseBlock() {}
-
- void BaseBlock::setLastChangedParam(const Param& last_param, bool user_provided) {}
-
- void BaseBlock::addParam(BlockDescriptor& block_data, const ParamDescriptor& in_param, const char* char_name){}
- param_handle_t BaseBlock::getHandleFromParam(const Param* param) const {return 0;}
-
- void BaseBlock::init(BlockDescriptor& descriptor, BlockDescriptor& base_descriptor, size_t block_size)
- {
- descriptor.mCurrentBlockPtr = this;
- }
-
- Param::Param(BaseBlock* enclosing_block)
- : mIsProvided(false)
- {
- const U8* my_addr = reinterpret_cast<const U8*>(this);
- const U8* block_addr = reinterpret_cast<const U8*>(enclosing_block);
- mEnclosingBlockOffset = (U16)(my_addr - block_addr);
- }
-
- bool BaseBlock::deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack){ return true; }
- bool BaseBlock::serializeBlock(Parser& parser, Parser::name_stack_t name_stack, const LLInitParam::BaseBlock* diff_block) const { return true; }
- bool BaseBlock::inspectBlock(Parser& parser, Parser::name_stack_t name_stack) const { return true; }
- bool BaseBlock::merge(BlockDescriptor& block_data, const BaseBlock& other, bool overwrite) { return true; }
- bool BaseBlock::validateBlock(bool emit_errors) const { return true; }
-
- 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)
+ ParamValue<LLUIColor>::ParamValue(const LLUIColor& color)
+ : super_t(color)
{}
- void TypedParam<LLUIColor>::setValueFromBlock() const
+ void ParamValue<LLUIColor>::updateValueFromBlock()
{}
- void TypedParam<LLUIColor>::setBlockFromValue()
- {}
-
- void TypeValues<LLUIColor>::declareValues()
+ void ParamValue<LLUIColor>::updateBlockFromValue(bool)
{}
bool ParamCompare<const LLFontGL*, false>::equals(const LLFontGL* a, const LLFontGL* b)
@@ -108,14 +78,15 @@ namespace LLInitParam
return false;
}
- 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)
+
+ ParamValue<const LLFontGL*>::ParamValue(const LLFontGL* fontp)
+ : super_t(fontp)
{}
- void TypedParam<const LLFontGL*>::setValueFromBlock() const
+ void ParamValue<const LLFontGL*>::updateValueFromBlock()
{}
- void TypedParam<const LLFontGL*>::setBlockFromValue()
+ void ParamValue<const LLFontGL*>::updateBlockFromValue(bool)
{}
void TypeValues<LLFontGL::HAlign>::declareValues()
@@ -127,10 +98,10 @@ namespace LLInitParam
void TypeValues<LLFontGL::ShadowType>::declareValues()
{}
- void TypedParam<LLUIImage*>::setValueFromBlock() const
+ void ParamValue<LLUIImage*>::updateValueFromBlock()
{}
- void TypedParam<LLUIImage*>::setBlockFromValue()
+ void ParamValue<LLUIImage*>::updateBlockFromValue(bool)
{}
bool ParamCompare<LLUIImage*, false>::equals(
@@ -180,7 +151,7 @@ namespace tut
LLUrlMatch match;
ensure("empty()", match.empty());
- match.setValues(0, 1, "http://secondlife.com", "Second Life", "", "", LLStyle::Params(), "", "", LLUUID::null);
+ match.setValues(0, 1, "http://secondlife.com", "", "Second Life", "", "", LLStyle::Params(), "", "", LLUUID::null);
ensure("! empty()", ! match.empty());
}
@@ -193,7 +164,7 @@ namespace tut
LLUrlMatch match;
ensure_equals("getStart() == 0", match.getStart(), 0);
- match.setValues(10, 20, "", "", "", "", LLStyle::Params(), "", "", LLUUID::null);
+ match.setValues(10, 20, "", "", "", "", "", LLStyle::Params(), "", "", LLUUID::null);
ensure_equals("getStart() == 10", match.getStart(), 10);
}
@@ -206,7 +177,7 @@ namespace tut
LLUrlMatch match;
ensure_equals("getEnd() == 0", match.getEnd(), 0);
- match.setValues(10, 20, "", "", "", "", LLStyle::Params(), "", "", LLUUID::null);
+ match.setValues(10, 20, "", "", "", "", "", LLStyle::Params(), "", "", LLUUID::null);
ensure_equals("getEnd() == 20", match.getEnd(), 20);
}
@@ -219,10 +190,10 @@ namespace tut
LLUrlMatch match;
ensure_equals("getUrl() == ''", match.getUrl(), "");
- match.setValues(10, 20, "http://slurl.com/", "", "", "", LLStyle::Params(), "", "", LLUUID::null);
+ match.setValues(10, 20, "http://slurl.com/", "", "", "", "", LLStyle::Params(), "", "", LLUUID::null);
ensure_equals("getUrl() == 'http://slurl.com/'", match.getUrl(), "http://slurl.com/");
- match.setValues(10, 20, "", "", "", "", LLStyle::Params(), "", "", LLUUID::null);
+ match.setValues(10, 20, "", "", "", "", "", LLStyle::Params(), "", "", LLUUID::null);
ensure_equals("getUrl() == '' (2)", match.getUrl(), "");
}
@@ -235,10 +206,10 @@ namespace tut
LLUrlMatch match;
ensure_equals("getLabel() == ''", match.getLabel(), "");
- match.setValues(10, 20, "", "Label", "", "", LLStyle::Params(), "", "", LLUUID::null);
+ match.setValues(10, 20, "", "Label", "", "", "", LLStyle::Params(), "", "", LLUUID::null);
ensure_equals("getLabel() == 'Label'", match.getLabel(), "Label");
- match.setValues(10, 20, "", "", "", "", LLStyle::Params(), "", "", LLUUID::null);
+ match.setValues(10, 20, "", "", "", "", "", LLStyle::Params(), "", "", LLUUID::null);
ensure_equals("getLabel() == '' (2)", match.getLabel(), "");
}
@@ -251,10 +222,10 @@ namespace tut
LLUrlMatch match;
ensure_equals("getTooltip() == ''", match.getTooltip(), "");
- match.setValues(10, 20, "", "", "Info", "", LLStyle::Params(), "", "", LLUUID::null);
+ match.setValues(10, 20, "", "", "", "Info", "", LLStyle::Params(), "", "", LLUUID::null);
ensure_equals("getTooltip() == 'Info'", match.getTooltip(), "Info");
- match.setValues(10, 20, "", "", "", "", LLStyle::Params(), "", "", LLUUID::null);
+ match.setValues(10, 20, "", "", "", "", "", LLStyle::Params(), "", "", LLUUID::null);
ensure_equals("getTooltip() == '' (2)", match.getTooltip(), "");
}
@@ -267,10 +238,10 @@ namespace tut
LLUrlMatch match;
ensure_equals("getIcon() == ''", match.getIcon(), "");
- match.setValues(10, 20, "", "", "", "Icon", LLStyle::Params(), "", "", LLUUID::null);
+ match.setValues(10, 20, "", "", "", "", "Icon", LLStyle::Params(), "", "", LLUUID::null);
ensure_equals("getIcon() == 'Icon'", match.getIcon(), "Icon");
- match.setValues(10, 20, "", "", "", "", LLStyle::Params(), "", "", LLUUID::null);
+ match.setValues(10, 20, "", "", "", "", "", LLStyle::Params(), "", "", LLUUID::null);
ensure_equals("getIcon() == '' (2)", match.getIcon(), "");
}
@@ -283,10 +254,10 @@ namespace tut
LLUrlMatch match;
ensure("getMenuName() empty", match.getMenuName().empty());
- match.setValues(10, 20, "", "", "", "Icon", LLStyle::Params(), "xui_file.xml", "", LLUUID::null);
+ match.setValues(10, 20, "", "", "", "", "Icon", LLStyle::Params(), "xui_file.xml", "", LLUUID::null);
ensure_equals("getMenuName() == \"xui_file.xml\"", match.getMenuName(), "xui_file.xml");
- match.setValues(10, 20, "", "", "", "", LLStyle::Params(), "", "", LLUUID::null);
+ match.setValues(10, 20, "", "", "", "", "", LLStyle::Params(), "", "", LLUUID::null);
ensure("getMenuName() empty (2)", match.getMenuName().empty());
}
@@ -299,10 +270,10 @@ namespace tut
LLUrlMatch match;
ensure("getLocation() empty", match.getLocation().empty());
- match.setValues(10, 20, "", "", "", "Icon", LLStyle::Params(), "xui_file.xml", "Paris", LLUUID::null);
+ match.setValues(10, 20, "", "", "", "", "Icon", LLStyle::Params(), "xui_file.xml", "Paris", LLUUID::null);
ensure_equals("getLocation() == \"Paris\"", match.getLocation(), "Paris");
- match.setValues(10, 20, "", "", "", "", LLStyle::Params(), "", "", LLUUID::null);
+ match.setValues(10, 20, "", "", "", "", "", LLStyle::Params(), "", "", LLUUID::null);
ensure("getLocation() empty (2)", match.getLocation().empty());
}
}